DEV Community

Cover image for Why we should not mutate state in react
Ujjawal Kumar
Ujjawal Kumar

Posted on • Updated on

Why we should not mutate state in react

In React, it is generally considered a best practice not to mutate the state variable directly. Instead, you should use the setState method provided by React to update the state. There are several reasons why mutating the state directly is discouraged:

1. Predictable Behavior: React relies on the state and props to determine when to re-render components. By using the setState method, React can accurately track changes to state and trigger re-renders when necessary. If you mutate the state directly, React may not detect the change and fail to update the component properly.

2. Immutability and Immutability Benefits: React promotes the concept of immutable data. Immutable data means that once created, the data cannot be changed. When you mutate the state directly, you are breaking this principle and introducing potential side effects and unexpected behavior.

3. Performance Optimizations: React can perform optimizations like shallow comparisons to determine if a component needs to re-render. Immutability helps with these optimizations, as React can quickly check if the reference to the state has changed. If you mutate the state directly, React might assume the state has not changed and skip re-rendering when it's necessary.

4. Undo/Redo and Time-Travel Debugging: Immutability makes it easier to implement features like undo/redo functionality and time-travel debugging, as you can keep track of previous states without worrying about mutations.

5. Functional Programming Paradigm: Immutability aligns with the principles of functional programming, making your code easier to reason about and less prone to bugs.

Example

Let's create a simple component that will reverse the array when we click the button and display it in the UI.

Wrong way of reversing the array:

import { useState } from 'react';

const initialArr = [1, 2, 3, 4, 5];

export default function List() {
// state variable and method to update it
  const [arr, setArr] = useState(initialArr);

// function to reverse the array on button click
  function handleClick() {
    arr.reverse();    // mutating the state directly
    setArr(arr);
  }

  return (
    <>
      <button onClick={handleClick}>
        Reverse
      </button>
      <ul>
        {arr.map(item => (
          <li key={item}>{item}</li>
        ))}
      </ul>
    </>
  );
}
Enter fullscreen mode Exit fullscreen mode

Output:

Image description

Reason:

Array.prototype.reverse() mutates the original array in place, and then we are setting the state with the same array reference using setArr(arr). In React, when the state is set with the same reference, React doesn't detect the change, and it won't trigger a re-render.

Right Approach to Reverse:

import { useState } from 'react';

const initialArr = [1, 2, 3, 4, 5];

export default function List() {
// state variable and method to update it
  const [arr, setArr] = useState(initialArr);

// function to reverse the array on button click
  function handleClick() {
    const newArr = [...arr]; // creating new array and then reversing
    newArr.reverse()
    setArr(newArr);
  }

  return (
    <>
      <button onClick={handleClick}>
        Reverse
      </button>
      <ul>
        {arr.map(item => (
          <li key={item}>{item}</li>
        ))}
      </ul>
    </>
  );
}
Enter fullscreen mode Exit fullscreen mode

Output:

Image description

Reason

This code will work as expected. By creating a new array newArr and then reversing it using newArr.reverse(), we ensure that the original arr state is not modified directly. Instead, you update the state with a new reference, which will trigger a re-render in React and reflect the reversed array in the UI.

Hope you liked the article, if yes then give a like.

Have a Look: https://www.youtube.com/@endeavourmonk

Top comments (13)

Collapse
 
aarone4 profile image
Aaron Reese

What has never been particularly clear to me is where to draw the line on immuatbility. Let's say I have a customer object with an array of order objects and I need to update one of the orders. Do I just replace the order, the array of orders or the whole customer object. The latter seems very expensive for a single element of a single object in a nested array.
If my State has a customer and a courier and a finance company, can I just replace the customer or do I need to replace the entire State.

Collapse
 
endeavourmonk profile image
Ujjawal Kumar

When dealing with nested objects or arrays, it's generally recommended to maintain immutability at all levels. If you have a customer object with an array of order objects, and you need to update one order, you should create a new array with the updated order and use that to update the customer object. Similarly, if you need to update a property within an order object, create a new order object with the updated property and use it to create a new array of orders.

Collapse
 
aarone4 profile image
Aaron Reese

So in React specifically is every useState() creating a separate top level object or can the hook relate to a nested object.
I e. Could my useState refer to customer.orders and any setState(orders) would then also fire updates on the customer state too?

Thread Thread
 
endeavourmonk profile image
Ujjawal Kumar

In React, the useState hook is used to create a state variable and its associated setter function. Each call to useState creates a separate state variable, and these state variables can hold different data types, including nested objects.

Collapse
 
ant_f_dev profile image
Anthony Fung

In general, I agree on the concept of avoiding mutating data where possible. In one project I previously worked on, we used immutable-js to help with making data immutable.

Collapse
 
endeavourmonk profile image
Ujjawal Kumar

Nice, I have not used immutable-js yet, will try in future, thanks for suggesting

Collapse
 
remusdb profile image
Remus D. Buhaianu

Great, concise post, Ujjawal!

This can save junior developers a lot of time because it's one of the most common mistakes they make early on with React.

What do you think are some other common mistakes they make besides mutating the state directly?

Collapse
 
endeavourmonk profile image
Ujjawal Kumar

Thanks so much

Collapse
 
k3cman profile image
Nemanja Kecman

This is important for whole javascript ecosystem. If you mutate anything the pointer stays in memory and garbage collector cant remove it from memory, its nit just for React 🙂

Collapse
 
malikbilal111 profile image
Bilal Asghar

Great Post 🧡

Collapse
 
endeavourmonk profile image
Ujjawal Kumar

thanks

Collapse
 
miketalbot profile image
Mike Talbot ⭐

Immutability is certainly a quick way to test if two objects are the same or not, that can be an expensive operation. There are downsides though, garbage collection is not free and in rapid changing updates it's important to optimise for this to avoid glitches and stalls caused by constantly allocating and copying data.

The React useState mechanism can be pressed into service to give fine grained control over re-rendering while mutating underlying data structures and this can be optimal if the copying of a large data structure is required to maintain immutability.

Collapse
 
yeltazerka profile image
yeltazerka

Nice Read