React js Immutability Practice Questions with Solutions

Introduction

Immutability is an important concept in React state management. It means that instead of directly changing an existing array or object, you create a new value with the required changes. React applications commonly use the spread operator, map(), and filter() to perform immutable updates. In this chapter, we will practice immutable updates for objects, arrays, nested data, and common React state situations. React js Immutability Practice Questions with Solutions help to understand the concepts.

1. What is Immutability in React?

Immutability means treating existing state values as read-only and creating a new value when you need to make a change.

For example, instead of changing an existing object:

user.name = "Amit";

create a new object:

setUser((currentUser) => ({
  ...currentUser,
  name: "Amit"
}));

The second approach creates a new object rather than directly modifying the existing state.


2. Why is Immutability Important in React?

Immutability helps keep state updates predictable and makes it easier for React and developers to reason about what changed.

For example:

const [user, setUser] = useState({
  name: "Rahul",
  age: 22
});

Instead of:

user.age = 23;

use:

setUser((currentUser) => ({
  ...currentUser,
  age: 23
}));

The new object has a different reference from the previous object.

This approach is especially important when working with state-dependent rendering and optimizations such as React.memo.


3. How do you Update an Object Immutably in React?

Use the spread operator to create a new object.

Solution:

import { useState } from "react";

function App() {
  const [user, setUser] = useState({
    name: "Rahul",
    age: 22
  });

  function updateAge() {
    setUser((currentUser) => ({
      ...currentUser,
      age: 23
    }));
  }

  return (
    <div>
      <p>
        {user.name} - {user.age}
      </p>

      <button onClick={updateAge}>
        Update Age
      </button>
    </div>
  );
}

export default App;

The spread operator copies the existing properties into a new object.


4. How do you Update an Array Immutably in React?

You can use the spread operator to create a new array when adding an item.

Solution:

import { useState } from "react";

function App() {
  const [items, setItems] = useState([
    "Apple",
    "Mango"
  ]);

  function addItem() {
    setItems((currentItems) => [
      ...currentItems,
      "Banana"
    ]);
  }

  return (
    <div>
      <button onClick={addItem}>
        Add Banana
      </button>

      {items.map((item) => (
        <p key={item}>{item}</p>
      ))}
    </div>
  );
}

export default App;

The expression:

[...currentItems, "Banana"]

creates a new array.

The original array is not modified.


5. How do you Remove an Array Item Immutably?

The filter() method can create a new array without the item that needs to be removed.

Solution:

import { useState } from "react";

function App() {
  const [items, setItems] = useState([
    "Apple",
    "Mango",
    "Banana"
  ]);

  function removeItem(itemToRemove) {
    setItems((currentItems) =>
      currentItems.filter(
        (item) => item !== itemToRemove
      )
    );
  }

  return (
    <div>
      {items.map((item) => (
        <div key={item}>
          <span>{item}</span>

          <button
            onClick={() => removeItem(item)}
          >
            Remove
          </button>
        </div>
      ))}
    </div>
  );
}

export default App;

filter() returns a new array, so the existing state array is not directly modified.


6. How do you Update an Array Item Immutably?

The map() method can be used to create a new array and replace only the item that needs to change.

Solution:

import { useState } from "react";

function App() {
  const [items, setItems] = useState([
    "Apple",
    "Mango",
    "Banana"
  ]);

  function updateItem() {
    setItems((currentItems) =>
      currentItems.map((item) =>
        item === "Mango"
          ? "Orange"
          : item
      )
    );
  }

  return (
    <div>
      <button onClick={updateItem}>
        Change Mango
      </button>

      {items.map((item) => (
        <p key={item}>{item}</p>
      ))}
    </div>
  );
}

export default App;

The original array remains unchanged.

map() returns a new array containing the updated value.


7. How do you Update a Nested Object Immutably?

When updating nested state, copy each object level that you are changing.

Solution:

import { useState } from "react";

function App() {
  const [user, setUser] = useState({
    name: "Rahul",
    address: {
      city: "Delhi",
      country: "India"
    }
  });

  function updateCity() {
    setUser((currentUser) => ({
      ...currentUser,
      address: {
        ...currentUser.address,
        city: "Mumbai"
      }
    }));
  }

  return (
    <div>
      <p>Name: {user.name}</p>
      <p>City: {user.address.city}</p>

      <button onClick={updateCity}>
        Update City
      </button>
    </div>
  );
}

export default App;

Here, both the outer user object and the nested address object receive new references.

The country property is preserved.


8. Why Should You Avoid Methods like push(), pop(), and splice() on State Arrays?

Methods such as push(), pop(), and splice() modify the existing array.

For example, avoid:

items.push("Orange");
setItems(items);

Instead, create a new array:

setItems((currentItems) => [
  ...currentItems,
  "Orange"
]);

For removing an item, use filter():

setItems((currentItems) =>
  currentItems.filter((item) => item !== "Orange")
);

For replacing an item, use map():

setItems((currentItems) =>
  currentItems.map((item) =>
    item === "Orange" ? "Apple" : item
  )
);

The goal is not that these JavaScript methods are always bad. The important point is to avoid mutating the existing state value directly.


9. How do You Copy an Object without Mutating the Original State?

You can use the spread operator to create a shallow copy of an object.

Solution:

const user = {
  name: "Rahul",
  age: 22
};

const updatedUser = {
  ...user,
  age: 23
};

console.log(user);
console.log(updatedUser);

The original object remains:

{
  name: "Rahul",
  age: 22
}

The new object becomes:

{
  name: "Rahul",
  age: 23
}

However, remember that object spread creates a shallow copy. Nested objects are not automatically deep-copied.


10. How do you Build a Practical Immutable Todo List?

A Todo List is a good example of immutable state updates because it requires adding, updating, and removing objects from an array.

Solution:

import { useState } from "react";

function TodoApp() {
  const [todos, setTodos] = useState([
    {
      id: 1,
      text: "Learn React",
      completed: false
    },
    {
      id: 2,
      text: "Practice JavaScript",
      completed: false
    }
  ]);

  function addTodo() {
    const newTodo = {
      id: Date.now(),
      text: "Build a Project",
      completed: false
    };

    setTodos((currentTodos) => [
      ...currentTodos,
      newTodo
    ]);
  }

  function toggleTodo(id) {
    setTodos((currentTodos) =>
      currentTodos.map((todo) =>
        todo.id === id
          ? {
              ...todo,
              completed: !todo.completed
            }
          : todo
      )
    );
  }

  function removeTodo(id) {
    setTodos((currentTodos) =>
      currentTodos.filter(
        (todo) => todo.id !== id
      )
    );
  }

  return (
    <div>
      <h2>Todo List</h2>

      <button onClick={addTodo}>
        Add Todo
      </button>

      {todos.map((todo) => (
        <div key={todo.id}>
          <p>
            {todo.text} -{" "}
            {todo.completed
              ? "Completed"
              : "Pending"}
          </p>

          <button
            onClick={() => toggleTodo(todo.id)}
          >
            Toggle
          </button>

          <button
            onClick={() => removeTodo(todo.id)}
          >
            Remove
          </button>
        </div>
      ))}
    </div>
  );
}

export default TodoApp;

This example uses immutable update patterns throughout the application.

Adding:

setTodos((currentTodos) => [
  ...currentTodos,
  newTodo
]);

Updating:

setTodos((currentTodos) =>
  currentTodos.map((todo) =>
    todo.id === id
      ? {
          ...todo,
          completed: !todo.completed
        }
      : todo
  )
);

Removing:

setTodos((currentTodos) =>
  currentTodos.filter(
    (todo) => todo.id !== id
  )
);

These patterns are commonly used in Todo Apps, Shopping Carts, Product Lists, Dashboards, and other React applications.

Key Takeaways

  • Immutability means treating existing state values as read-only.
  • Do not directly mutate React state objects or arrays.
  • Use the spread operator to create new objects and arrays.
  • Use map() to update items in an array.
  • Use filter() to remove items from an array.
  • Nested objects require copying the levels that are being changed.
  • Array methods such as push(), pop(), and splice() should not be used to directly mutate state arrays.
  • Object spread creates a shallow copy, not a deep copy.
  • Functional state updaters are useful when the new state depends on the previous state.
  • Immutable update patterns make React state easier to reason about and work well with reference-based optimizations.

FAQs

1. What is Immutability in React?

Immutability means not directly changing an existing state object or array. Instead, create a new value containing the required changes.

2. Why should React state not be mutated directly?

Direct mutation can make state changes harder to track and can lead to unexpected behavior. Creating new values provides a predictable state update pattern.

3. How do you update an object immutably in React?

Use the spread operator:

setUser((currentUser) => ({
  ...currentUser,
  name: "Amit"
}));

4. How do you update an array immutably in React?

Use methods that return new arrays, such as the spread operator, map(), and filter().

5. Can I use push() with a React state array?

You should not use push() to directly modify the existing state array. Create a new array instead.

6. Does the spread operator create a deep copy?

No. Object and array spread create shallow copies. Nested objects or arrays still need to be copied separately when you need to update them immutably.

7. What is the difference between mutation and immutable updates?

Mutation changes the existing object or array. An immutable update creates a new object or array while leaving the existing state value unchanged.

Written by Shubhranshu Shekhar, who has trained 20000+ students in coding.

Scroll to Top