React.js course ยท Lecture 12 of 27

Managing Array State & Immutable Updates

A masterclass tutorial covering Adding items, Removing items, Updating items, Array.prototype.map(), Array.prototype.filter(), Spread operator for arrays, Immutable array update patterns, Pitfalls, and Interview Questions.

  • 30 min read
  • Intermediate
  • 8 topics
  • 20 Sept 2026
  • Ravindra Nath Jha

What is Array State in React?

In web development, we frequently store arrays in component state (lists of todo items, shopping cart items, notification queues, message histories).

Just like objects, arrays in React state MUST be updated immutably without modifying the original array reference.

Mutating vs Non-Mutating Array Methods

Action TypeMutating Methods (โŒ DO NOT USE DIRECTLY)Non-Mutating Methods (โœ… USE IN REACT STATE)
Adding Itemspush(), unshift()Spread operator [...arr, newItem], concat()
Removing Itemspop(), shift(), splice()filter()
Updating ItemsDirect index assignment arr[i] = valmap()
Sorting / Reversingsort(), reverse()toSorted(), [...arr].sort()

Pattern 1 โ€” Adding Items to Array State

Use the spread operator ... to create a new array containing all old items plus the new item:

JavaScript
// Add item to END of array:
setItems(prev => [...prev, newItem]);

// Add item to START of array:
setItems(prev => [newItem, ...prev]);

Pattern 2 โ€” Removing Items from Array State

Use Array.prototype.filter() to return a new array excluding the target item ID:

JavaScript
const handleDeleteItem = (idToDelete) => {
  setItems(prev => prev.filter(item => item.id !== idToDelete));
};

Pattern 3 โ€” Updating Specific Items in Array State

Use Array.prototype.map() to transform the target item while keeping all other items unchanged:

JavaScript
const handleToggleComplete = (targetId) => {
  setItems(prev => prev.map(item => {
    if (item.id === targetId) {
      return { ...item, completed: !item.completed }; // Return NEW updated object
    }
    return item; // Return unmodified item
  }));
};

Common Mistakes with Array State

  1. Using push() or splice() directly: items.push(newItem); setItems(items); mutates existing memory reference and fails to trigger re-renders!
  2. Mutating objects inside array during map(): item.completed = true; return item;. Always return a fresh object: { ...item, completed: true }.
  3. Sorting array state in-place: items.sort() mutates original array. Use [...items].sort().

Top React Interview Questions on Array State

Q1: How do you append an item to an array in React state without mutating it?

*Answer*: Use the spread operator: setItems(prev => [...prev, newItem]).

Q2: Why should you use filter() instead of splice() to delete array state items?

*Answer*: splice() mutates the array in-place, whereas filter() returns a brand new array reference without altering original data.

Q3: How do you update a single item property inside an array of objects?

*Answer*: Use map() to locate the matching item ID and return a new object with spread properties ({ ...item, prop: val }).

Practical Hands-On Exercise โ€” Complete Interactive Todo Manager

Here is a complete interactive todo application demonstrating adding, toggling completion, deleting, and filtering array state:

JavaScript
// Complete Masterclass Array State Showcase (src/App.jsx)
import React, { useState } from 'react';

export default function App() {
  const [todos, setTodos] = useState([
    { id: 1, text: "Learn React Component Architecture", completed: true },
    { id: 2, text: "Master State Immutability & Array Updates", completed: false }
  ]);
  const [taskText, setTaskText] = useState("");

  // Add Todo Handler
  const handleAddTodo = (e) => {
    e.preventDefault();
    if (!taskText.trim()) return;
    const newTodo = {
      id: Date.now(),
      text: taskText,
      completed: false
    };
    setTodos(prev => [...prev, newTodo]);
    setTaskText("");
  };

  // Toggle Completion Handler
  const handleToggle = (id) => {
    setTodos(prev => prev.map(todo => 
      todo.id === id ? { ...todo, completed: !todo.completed } : todo
    ));
  };

  // Delete Todo Handler
  const handleDelete = (id) => {
    setTodos(prev => prev.filter(todo => todo.id !== id));
  };

  return (
    <div style={{ maxWidth: "550px", margin: "30px auto", padding: "24px", background: "#ffffff", border: "1px solid #cbd5e1", borderRadius: "16px" }}>
      <h2 style={{ color: "#002057" }}>Lecture 12: Array State Masterclass Todo App</h2>

      <form onSubmit={handleAddTodo} style={{ display: "flex", gap: "10px", marginBottom: "20px" }}>
        <input 
          type="text" 
          value={taskText}
          onChange={(e) => setTaskText(e.target.value)}
          placeholder="Add a new masterclass task..."
          style={{ flex: 1, padding: "10px", borderRadius: "6px", border: "1px solid #cbd5e1" }}
        />
        <button type="submit" style={{ background: "#2506ad", color: "#fff", border: "none", padding: "10px 18px", borderRadius: "6px", fontWeight: "bold" }}>
          Add Task
        </button>
      </form>

      <ul style={{ listStyle: "none", padding: 0 }}>
        {todos.map(todo => (
          <li key={todo.id} style={{ display: "flex", justifyContent: "space-between", alignItems: "center", padding: "12px", borderBottom: "1px solid #f1f5f9" }}>
            <span 
              onClick={() => handleToggle(todo.id)}
              style={{ 
                textDecoration: todo.completed ? "line-through" : "none",
                color: todo.completed ? "#94a3b8" : "#0f172a",
                cursor: "pointer",
                fontWeight: "500"
              }}
            >
              {todo.completed ? "โœ… " : "โญ• "} {todo.text}
            </span>
            <button onClick={() => handleDelete(todo.id)} style={{ background: "#ef4444", color: "#fff", border: "none", padding: "6px 12px", borderRadius: "4px", cursor: "pointer" }}>
              Delete
            </button>
          </li>
        ))}
      </ul>
    </div>
  );
}