DEV Community

Discussion on: Learn To Clone Like A Sith Lord

Collapse
 
bytebodger profile image
Adam Nathaniel Davis • Edited

This is a good point! And, no, the answer is that they are not updating the original state. This happens for several reasons.

First, state is a "special" kinda protected instance in React. Once it's created, you can only update it via this.setState() (in class-based components), or via the custom setter that was created in the useState() Hook (in function-based components). [Little trivia fact here: Even if you're using useState() in a function-based component, it's still using setState() under the covers.]

Second, you have to think about the type of data, and the structure of data that is usually saved in state. Specifically, what I'm talking about is the fact that the state object is usually holding only scalar values. This also means that state data is often only one "layer' deep. This means that you can safely clone it with just a spread operator.

Look at the first example given above under "Spread Operators". This works fine:

let phantomMenace = { master: 'palpatine', apprentice: 'maul' };
const attackOfTheClones = {...phantomMenace};

attackOfTheClones is a true clone of phantomMenace - because phantomMenace contained only a single layer of simple, scalar values. But this does not work fine:

let phantomMenace = { 
  master: 'palpatine', 
  apprentice: 'maul',
  henchmen: {
    one: 'nute gunray',
    two: 'rune haako',
  },
};
const attackOfTheClones = {...phantomMenace};

Now, attackOfTheClones is no longer a true clone, because part of its data - the henchmen object - is a pointer referring back to the original object. In this case, a simple spread operator failed to achieve the objective.

Now think about what you typically see in state values:

export default class Foo extends React.Component {
  state = {
    isLoggedIn: false,
    username: '',
    showLeftNav: true,
  };

  render = () => {
    return (<div>...all the display in here...</div>);
  }
}

In this scenario, if we were to do this: const newObject = {...this.state}; would it result in a perfect clone that has no ties back to the original object? Yes. It would.

Here's another way to think about it. This always creates a fresh, new object: const newObject = {...oldObject}; However, sometimes the oldObject has additional data structures nested inside it. In that case, the additional data structures won't be cloned with a simple spread operator. The newObject will just get a pointer to the additional data structures that are nested inside of oldObject.

So when you are dealing with a "typical" state object that looks like this:

state = {
  isLoggedIn: false,
  username: '',
  showLeftNav: true,
};

The spread operator will always give you a nice, clean clone because there are no nested data structures.

Some comments have been hidden by the post's author - find out more