skip to Main Content

Modifying the first of item in an array – two ways:

  • one modifies the items directly
  • one creates a copy of the array

Which way is the correct way to do it?

const [testArray, setTestArray] = React.useState([{ name: 'value0' }, { name: 'value1' }]);

const handleTest = () => {
    setTestArray((prev) => {
        const firstItem = prev[0];
        const updatedFirstItem = { ...firstItem, name: 'newValue' };
        prev[0] = updatedFirstItem;
        return prev;
    });
};

const handleTest2 = () => {
    setTestArray((prev) => {
        const copyOfArray = [...prev];
        const firstItem = copyOfArray[0];
        const updatedFirstItem = { ...firstItem, name: 'newValue' };
        copyOfArray[0] = updatedFirstItem;
        return copyOfArray;
    });
};

From a purely "at the surface"… looking array and its values… both should return an array with the same values (to be clear, not talking about objects nor references in memory, just looking at the surface of an array and its values).

But from a Virtual DOM / and React reconciliation perspective, is there a preferred way to do this?

2

Answers


  1. Chosen as BEST ANSWER

    You want the second one - make a copy and then update the copy.


    From the React docs:

    State can hold any kind of JavaScript value, including objects. But you shouldn’t change objects that you hold in the React state directly. Instead, when you want to update an object, you need to create a new one (or make a copy of an existing one), and then set the state to use that copy.

    In JavaScript, arrays are just another kind of object. Like with objects, you should treat arrays in React state as read-only. This means that you shouldn’t reassign items inside an array like arr[0] = 'bird', and you also shouldn’t use methods that mutate the array, such as push() and pop().

    So when working with array objects - do the ones on the right column.

    avoid (mutates the array) prefer (returns a new array)
    adding push, unshift concat, [...arr] spread syntax (example)
    removing pop, shift, splice filter, slice (example)
    replacing splice, arr[i] = ... assignment map (example)
    sorting reverse, sort copy the array first (example)

    Here is the issue with the the first one. In the first one, you are returning prev but that refers to the same array object as the previous state + because you reassigned an item inside an array - a refresh will not be triggered.

    Arrays are mutable in JavaScript, but you should treat them as immutable when you store them in state. Just like with objects, when you want to update an array stored in state, you need to create a new one (or make a copy of an existing one), and then set state to use the new array.

    Read more on React docs.


    It seems like a waste of memory to be making copies every time - but my understanding now is that in the React rendering process it compares previous virtual DOM to the new virtual DOM to determine whether to update/re-render parts of the screen.

    And if the framework team did NOT do this (ie. if they did not generally prefer new copies then they would have to be much more thorough in state comparisons); so for example, in cases where there was a long array, they would have to go item-by-item to compare for updates/changes which would be expensive/slow.

    So my understanding here is that the framework team made a trade-off of more memory usage for the sake of speed.



  2. This answer that starts with:

    You want the second one – make a copy and then update the copy.

    is correct.


    However, for a deeper understanding, it might be worth noting that a similar principle also applies to objects and not just array objects.

    The long story short is this.

    1/ React does many things under the hood – the most relevant is the reconciliation process between previous and new virtual DOM to figure out which parts of the screen to re-paint

    2/ Assuming data immutability when comparing state previous and current state makes React’s updating fast/efficient. Ref here

    3/ React uses Javascript under the hood

    4/ Javascript has equality comparators and it uses Object.is() to determine if the new state is equivalent to old state. Ref here

    5/ Object.is() is not equal to ===. I mention this because many explainer articles use the two interchangeably. Ref here and here


    If you’d like to do more further reading on immutability, you can do so here and here and here, other ways of comparing objects in Javascript here, and the React doc on updating objects in state is here.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search