← 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.
Syntax
markup
setObject({ ...object, property: newValue });          // update an object
setArray([...array, newItem]);                         // add
setArray(array.filter((item) => item.id !== id));      // remove

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>
Related Topics
{# common_mistakes/chapter_summary/browser_support: on Hindi pages the view already swaps in the hi_ translation fields (or blanks these out if untranslated), so this renders correctly for both languages without a lang_code check here. #}
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.