Question about chapter "Pagination, Filtering, and Sorting" course nr 22

i have a question about the function is made to handle like button. why is line 7 important i have the feeling its double work bcs we already duplicate our state date in line 2

 1    handleLike = (movie) =>{
2        const movies = [...this.state.movies];
3       
4
5        const index = movies.indexOf(movie);
6
7        movies[index] = {...movies[index]};
8        movies[index].liked = !movies[index].liked;
9        this.setState({movies});
        

     }

I think this is a problem with references. Before line 7, movies[index] still has a reference to the object pointed to by the this.state.movies[index]. The copy at line 2 probably just copies the references to any nested objects (in a new object). After line 7, movies[index] refers to a new object that is a copy of the original.

That is my best explanation based on how similar code would work in Java, but I am not 100% certain it works the same in React (just my best guess).

1 Like

Hi

Here is how I understand it.
movies is a copy, right ?
So why wouldn’t we work on that copy directly ?
But it is a const. To me the question is more like “Why is it a const ?”

Anyway, in the current state of the code, you cannot change movies reference but you want to modify its content. So movies[index] simply points to the entry you want to work with.

Something I would try:

  • Removing the const on line 2
  • Comment line 7
  • Try

Tell us how it behaves.

Regards.

Could you tell me the number of the lesson so I check again ?

Clarifying a bit: I assume React does a shallow copy by default. A deep copy would mean that there is no relationship at all between the copy and the original (new objects all the way down). With a shallow copy, any references copied from the original object would still point to the same objects as the original.

Again, I am not super familiar with React so I could be wrong about how these copies work. I believe React is a layer on top of JavaScript so I would generally believe the behavior is similar to Java (but even that is hardly a guarantee of similarity).

I am not english native speaker and I meet that shallow and deep copy terms from time to time and I am not sure to understand.

From what you write it seems to be creating a new identifier pointing to the same ref against creating a real copy.

Such as :

MyRefType original = new RefType();
MyRefType shallowCopy = original;
MyRefType deepCopy = original.Clone();

Is it correct?

If that were Java at least, the second line is just making a new reference to the original object. So original and shallowCopy would point to the exact same object (no copy is made at all). I am guessing that this is C++ which may have different semantics, though I would be surprised if that second line created a new object.

Let me create an artificial case to help explain the difference between a shallow copy and a deep copy. Suppose we have the following two classes:

class Toy {
  private static int nextId = 0;
  private final int id;
  Toy() {
    this.id = nextId++;
  }
}

class ToyBox {
  private final List<Toy> toys = new ArrayList<>();
  public void addToy(Toy toy) { toys.add(toy); }
}

And let us make a couple of Toy objects and put them into the ToyBox:

class Main {
  public static void main(String[] args) {
    var toyBox = new ToyBox();
    var toy0 = new Toy();
    toyBox.addToy(toy0);
    var toy1 = new Toy();
    toyBox.addToy(toy1);
  }
}

So we have three objects in total: one ToyBox and two Toy objects.

Now, if we make a shallow copy of the ToyBox it will create a new ToyBox object, but that ToyBox would contain references to the existing Toy objects. This is the exact behavior that the clone() method has in Java. So writing that out:

class ToxBox implements Cloneable {
  ... // existing implementation
  public ToyBox shallowClone() {
    return super.clone();
  }
}

class Main {
  public static void main(String[] args) {
    ... // existing implementation
    var shallowClone = toyBox.shallowClone();
  }
}

If we want a deep copy, we have to make a copy of each Toy inside the ToyBox as well:

class Toy implements Cloneable {
  ... // existing implementation
}

class ToyBox implements Cloneable {
  ... // existing implementation
  @Override
  public ToyBox clone() {
    var clone = shallowClone();
    clone.toys.clear();
    for (var toy : this.toys) {
      clone.addToy(toy.clone());
    }
    return clone;
  }
}

class Main {
  public static void main(String[] args) {
    var toyBox = new ToyBox();
    var toy0 = new Toy();
    toyBox.addToy(toy0);
    var toy1 = new Toy();
    toyBox.addToy(toy1);
    var shallowClone = toyBox.shallowClone();
    var deepClone = toyBox.clone();
  }
}

I reproduced the full main method so that we can run through the whole state of affairs. After this code executes, there will be 3 ToyBox objects and 4 Toy objects. The toyBox has two toys (toy0 and toy1). Since shallowClone is a shallow copy of toyBox it is a distinct object (ie. toyBox != shallowCopy), but it has the exact same toys (toy0 and toy1). Finally, deepClone is a deep copy of toyBox so it is a distinct object (toyBox != deepClone) and has two newly created toys that are clones of toy0 and toy1 (we will call them toy0Clone and toy1Clone). So toy0 != toy0Clone and toy1 != toy1Clone.

NOTE: If the Toy class had references to other objects, we would have to recursively make deep copies all the way down to the bottom until we had only primitive types.

Hopefully that helps clarify the difference between a deep copy and a shallow copy for you.

1 Like

Ok that’s clearer at least.
So a shallow copy is creating a new object (copy) yet inner reference type fields remain the same.
Deep copy is creating a new object (copy) and creating new instances for inner reference type fields.

It would look like this.

1 Like

Yes, you have it exactly correct there.

2 Likes

OK thanks for the help. :+1: