faraz
faraz

Reputation: 2733

is this actually mutating state? A bad practice?? - react

this is a piece of code I came across and I am not sure what it is doing actually? it feels to me like it is mutating the state Version 1:

 this.state.items = [...this.state.initialItems];

Now, is the above code mutating state??

And can the above also be written as Version 2:

this.setState({
   items:[...this.state.initialItems];
});

the thing is that version 1 is working but version 2 isnt. So,what's wrong here? what am I missing? what does ...this.state.initialItems do? is that the spread operator?

Upvotes: 0

Views: 617

Answers (3)

Hemerson Carlin
Hemerson Carlin

Reputation: 7424

Version 1 is mutating your state and this is a bad practice.

Version 2 is not and the reason why it's not working (actually it is) is that calls to setState are asynchronous and therefore you will not have access to its up-to-date value in the next line.

Should you need access to the new state value, you can use:

this.setState(prevState => ({
  items: [...prevState.initialItems],
}), () => {
  console.log(this.state) // new state value
})

Please also refer to this link in ReactJS docs about how to set state properly.

The ... is indeed the spread syntax.

You can use it, for example:

To add items to an array

const arr = [1, 2, 3, 4]
const newArr = [...arr, 5, 6] // This will spread everything in arr and add numbers 5 and 6 to newArr

console.log('arr', arr)
console.log('newArr', newArr)

To get items from an array

const arr = [1,2,3,4]
const [firstItem, secondItem, ...others] = arr

console.log('firstItem:', firstItem)
console.log('secondItem:', secondItem)
console.log('others:', others)

To create a new object

const obj = {
  name: 'Stack Overflow',
  age: 10,
}

const newObj = {
  ...obj, // gets all properties from obj
  age: 12, // but only change age
}

console.log('obj', obj)
console.log('newObj', newObj)

Upvotes: 1

Agney
Agney

Reputation: 19194

Version 1 is wrong. But the real question is Why? state is a special object in React and it is not to be mutated.

Say you declare an object:

const obj = {
  label: 'doe'
  value: 'foo'
}

Then, you update the obj with

obj.value = 'bar'

This is the process of mutation and should never perform it with a state variable. Instead we use setState as a request to change the state.

... is the spread syntax.

As the other answers have mentioned, the reason why you think it is not working might be because state updates are asynchronous. You have to use the setState callback or componentDidUpdate lifecycle hook to log the value of state.

setState(stateChange[, callback])

Another issue in the code is the use of this.state.something inside setState. Like we already mentioned before setState is asynchronous and a number of state updates may be merged. So using this.state.initialItems may not always work out for you, if you miss a update.

this.setState((prevState) => {
  return {items: [...prevState.initialItems]};
}); 

This would be the right way to use a value of previous state.

Upvotes: 1

kng
kng

Reputation: 627

This code is definitely mutating state. Don't do it:

this.state.items = [...this.state.initialItems];

Yes, the ... is a spread operator in this context.

For version 2, make sure you don't use the semicolon. It should just be :

this.setState({
   items:[...this.state.initialItems]
});

And of course like the poster below has said, it's asynchronous so you won't see the results right away.

Augmentation 1:
If you want to do things after your state is ready, you can add a callback as your 2nd argument to setState.

this.setState({
   items:[...this.state.initialItems]
  }, ()=>{
   // do stuff
  }
);

Augmentation 2:
Your first argument to setState can be a function as well, as the other poster has mentioned, so that when you access state it will be the "freshest" copy, and not the one that was captured when you created the object that you passed to setstate:

this.setState((stateNOW) => {
   return {items:[...stateNOW.initialItems]};
  }, ()=>{
   // do stuff
  }
);

Upvotes: 2

Related Questions