← Back to React Course | Chapter 4: State & Event Handling | Lesson 3 of 10

State with Objects and Arrays

Updating an object or array in state is like replacing a whole drawer with a new one instead of just moving things around inside the old drawer.

Updating an Object in State

To update one property of an object in state, spread the existing object into a new one and override just the property that changed. This creates a brand-new object reference, which is what lets React detect the update.

Note: The pattern setState({...state, propertyToChange: newValue}) covers the vast majority of object state updates.

Warning: Writing state.name = New; setState(state); mutates the old object and often fails to trigger a re-render.

Example: Updating an Object in State

markup
<!DOCTYPE html>
<html>
<head>
  <script src="https://unpkg.com/react@18/umd/react.development.js"></script>
  <script src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
  <script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
</head>
<body>
  <div id="root"></div>
  <script type="text/babel">
    function ProfileForm() {
      const [user, setUser] = React.useState({ name: "Sam", age: 25 });
      return (
        <div>
          <p>{user.name}, {user.age}</p>
          <button onClick={() => setUser({...user, age: user.age + 1})}>Birthday!</button>
        </div>
      );
    }
    ReactDOM.createRoot(document.getElementById('root')).render(<ProfileForm />);
  </script>
</body>
</html>

Adding to an Array in State

To add an item to an array in state, create a new array using the spread operator plus the new item, rather than calling .push() on the existing array. This keeps the update immutable and ensures React notices the change.

Note: setItems([...items, newItem]) is the standard pattern for adding to array state.

Warning: items.push(newItem); setItems(items); mutates the original array and can cause React to miss the update.

Example: Adding to an Array in State

markup
<!DOCTYPE html>
<html>
<head>
  <script src="https://unpkg.com/react@18/umd/react.development.js"></script>
  <script src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
  <script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
</head>
<body>
  <div id="root"></div>
  <script type="text/babel">
    function TodoList() {
      const [items, setItems] = React.useState(["Buy milk"]);
      function addItem() {
        setItems([...items, "New task " + (items.length + 1)]);
      }
      return (
        <div>
          <ul>{items.map((item, i) => <li key={i}>{item}</li>)}</ul>
          <button onClick={addItem}>Add Item</button>
        </div>
      );
    }
    ReactDOM.createRoot(document.getElementById('root')).render(<TodoList />);
  </script>
</body>
</html>

Removing from an Array in State

To remove an item, create a new array that filters out the unwanted item using .filter(), rather than using a mutating method like .splice(). .filter() naturally returns a new array containing only the items that pass the check.

Note: Filtering by a unique ID (rather than array index) is safer when items can be reordered or added.

Warning: items.splice(index, 1) mutates the array in place and is easy to misuse with React state.

Example: Removing from an Array in State

markup
<!DOCTYPE html>
<html>
<head>
  <script src="https://unpkg.com/react@18/umd/react.development.js"></script>
  <script src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
  <script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
</head>
<body>
  <div id="root"></div>
  <script type="text/babel">
    function TodoList() {
      const [items, setItems] = React.useState(["Buy milk", "Walk dog", "Read book"]);
      function removeItem(index) {
        setItems(items.filter((_, i) => i !== index));
      }
      return (
        <ul>
          {items.map((item, i) => <li key={i}>{item} <button onClick={() => removeItem(i)}>x</button></li>)}
        </ul>
      );
    }
    ReactDOM.createRoot(document.getElementById('root')).render(<TodoList />);
  </script>
</body>
</html>
Common Mistakes
  1. Calling .push(), .splice(), or similar mutating array methods directly on state.
  2. Modifying a nested property of a state object directly (state.user.name = New) instead of spreading it into a new object.
  3. Forgetting that spreading only copies one level deep -- nested objects/arrays still need their own spread.
Chapter Summary
  • Objects and arrays in state must be replaced with new copies, not mutated.
  • The spread operator (...) is the standard tool for copying and updating objects/arrays immutably.
  • Array methods like .map(), .filter(), and .concat() return new arrays instead of mutating.
  • Nested state requires spreading at every level that changes.
Browser Support

No browser-specific restrictions -- relies on standard JavaScript spread syntax (ES2018+).

Login to run this code

C/C++/Java/PHP execution requires a free account. Your code is saved — you'll land right back in the editor after logging in.