React - Immutability - Is cloning required in this handler function?

Hello, I have a question regarding immutability in React. I am following the tutorial by Mosh. I am trying to understand the reason for cloning an object in the code.

To give a brief intro, In the tutorial he is teaching how to toggle a like button, it is a font awesome icon, a screen shot of is attached below.

The data is stored as an array with objects. See the code below:

const movies = [
  {
    _id: "5b21ca3eeb7f6fbccd471815",
    title: "Terminator",
    genre: { _id: "5b21ca3eeb7f6fbccd471818", name: "Action" },
    numberInStock: 6,
    dailyRentalRate: 2.5,
    publishDate: "2018-01-03T19:04:28.809Z",
    movieLiked: false
  },
]

When the like button is clicked a handler function is used to update the ‘movieLiked’ property. See below:

   const handleLike = (movie) => {
    const tempMovies = [...movies];
    const index = tempMovies.indexOf(movie);
    tempMovies[index] = { ...tempMovies[index] }; //Is this line required to maintain immutability?
    tempMovies[index].movieLiked = !tempMovies[index].movieLiked;
    setMovies(tempMovies);
  };

In the video he clones the movies array to tempMovies array, this is to maintain immutability. After a few lines of code, he again clones the object at tempMovies[index], is this required for maintaining immutability because the code works similarly even without this line if code?

Hi, @KiranVaka!

I’m afraid this isn’t really a good example on immutability, because const tempMovies = [...movies] is only a shallow copy - if you tried to access any movie inside of the tempMovies array and modified any property, that change would reflect in the movies state, because both arrays are referencing same objects.

I would suggest to not access state directly in the handler function, but do so in the state setter function, that is setMovies. If you passed a callback to it, it would receive the latest version of state, which would allow you to do any desired state changes. For example:

const handleLike = (movieId) => {
  setMovies((prevMovies) => 
     prevMovies.map((movie) => 
        movie.id === movieId ? ({ ...movie, movieLiked: true }) : movie ))
}

Let me know if this example is clear to you!

Would need to see the full code for the component/s, bit I’m not sure this is a great example, complex objects + useState aren’t a great fit, I’m not sure where the tutor is going with this (I assume they’ll move on to showing an alternative, like the useReducer hook?). But anyway

So immutability means immutability, so yes, it is required if you want immutability.

However, as you’ve noticed it’s going to make no difference in your current situation.

React is calling functions [which call functions, which call functions, which…] over and over, so to try and do as little work as possible it’s designed to check if the argument/s to those functions are the same value or not – if they’re the same, don’t need to call the function.

Issue with objects is that, if you mutate any properties of the object, that doesn’t change the reference to the object itself: React code will happily say “yep, nothing’s changed, no need to rerun this function”.

So by making sure everything is immutable, that stops being a problem – if you update some properties of an object, you make a new object. React code sees it’s a different value, updates.

In this situation, you copy the object (an array in this case), mutate the properties, set the state to the copy. React registers that there’s been a setState call. Pass the new state in as an argument to the component function it’s attached to. State has definitely changed, it’s a new object, rerender.

(Conversely, if you mutated directly: you mutate the properties, set the state. React registers that there’s been a setState call. Pass the state in as an argument to the component function it’s attached to. State has not changed, it’s not new object, don’t rerender).

If that component rerenders, if its state actually changes in a way visible to React (via the new object), then by default so does everything under that component.

If you have some specific child component that depends on that specific (non-copied) property [possibly only if that component is memoised, but it depends on structure], mutating values on that object will then in turn mean that you get the problem described above – the array is a collection of objects, the reference to each object never changes, the component that depends on that specific object will not get new arguments.

Hope that kinda makes sense, as I say would need to see more code.

Overall, immutability tends to help make React code easier to grok (easier to see what will change and when) and as a rule of thumb generally more efficient. But JS doesn’t have immutable data structures, so doing it manually is fiddly and doesn’t always make any difference – this is why immutability libraries are relatively popular (not using big complex objects in state is normally easier tho)

Hello @vebradev ,

It makes sense now. I understand the example too, Thank You.

1 Like

Hello @DanCouper,

The tutor was using classes and state in the example. When I created react app, I saw the Function App(), instead of class App. So, I did limited research and though I would implement this and learn it as well.

Now, I understand it is not a good fit and I might have issues while I follow the future lectures. I will make the changes to my code.

Thank you for the explanation.

1 Like

From what i can gather, since as others stated, [...movies] is only a shallow copy, the purpose of { ...tempMovies[index] }, might be to also copy the particular object you want to modify, so it doesnt change the actual state object, but the copy. Before that, id imagine tempMovies[index] was reference to the particular object on that index and any following changes on that location, would mutate that particular object.
PS: while I agree this is bad practice and usign the callback function on the setState is grealy advisiable, maybe the purpose of this particular example is to paint how it would look like to do it before using the advanced notion of the callback and following would be introduction of that callback aproach.
There are also additional frameworks, which allow for mutating state(but preserving immutable original state under the hood), so you can, for conveniance, manipulate directly the state object, without actually harming it i nthe process and keeping things intact in the end
PS2: and just not to leave some doubts why we dont want to mutate the state- its so we dont accidentally perform the same action twice, and callback assists with always working with the actualy state, not some outdated reference

1 Like

Hello @Sylvant,

I understand, there are a lot more lectures to finish. I believe the callback approach will be introduced in those.