Everybody says don’t do it. Never mutate state directly, always call setState .

But why, though?

If you’ve tried it out, you might’ve noticed nothing bad happened. If you modify state directy, call this.setState({}) or even this.forceUpdate() , then everything might appear to be just fine.

this . state . cart . push ( item .id ) ; this . setState ({ cart : this . state .cart }) ; // renders like normal! maybe?

This is a bad idea for two reasons (even though it would work in this example, and many others).

(other patterns to avoid are things like this.state.something = x and this.state = x )

Mutating state directly can lead to odd bugs, and components that are hard to optimize. Here’s an example.

As you may already know, a common way to tune a React component for performance is to make it “pure,” which causes it to only re-render when its props change (instead of every time its parent re-renders). This can be done automatically by extending React.PureComponent instead of React.Component , or manually by implementing the shouldComponentUpdate lifecycle method to compare nextProps with current props. If the props look the same, it skips the render, and saves some time.

Here is a simple component that renders a list of items (notice that it extends React.PureComponent ):

class ItemList extends React . PureComponent { render () { return ( < ul > { this . props . items . map ( item => < li key = { item .id } > { item .value } </ li > ) } </ ul > ) ; } }

Now, here is a tiny app that renders the ItemList and allows you to add items to the list – the good way (immutably), and the bad way (by mutating state). Watch what happens.

class App extends Component { // Initialize items to an empty array state = { items : [] } ; // Initialize a counter that will increment // for each item ID nextItemId = 0 ; makeItem () { // Create a new ID and use // a random number as the value return { id : this .nextItemId ++ , value : Math . random () } ; } // The Right Way: // copy the existing items and add a new one addItemImmutably = () => { this . setState ({ items : [ ... this . state .items, this . makeItem () ] }) ; } ; // The Wrong Way: // mutate items and set it back addItemMutably = () => { this . state . items . push ( this . makeItem ()) ; this . setState ({ items : this . state .items }) ; } ; render () { return ( < div > < button onClick = { this .addItemImmutably } > Add item immutably (good) </ button > < button onClick = { this .addItemMutably } > Add item mutably (bad) </ button > < ItemList items = { this . state .items } /> </ div > ) ; } }

Try it out!

Click the immutable Add button a few times and notice how the list updates as expected.

Then click the mutable Add button and notice how the new items don’t appear, even though state is being changed.

Finally, click the immutable Add button again, and watch how the ItemList re-renders with all the missing (mutably-added) items.

This happens because ItemList is pure, and because pushing a new item on the this.state.items array does not replace the underlying array. When ItemList is asked to re-render, it will notice that its props haven’t changed and it will not re-render.

Recap

So there you go: that’s why you shouldn’t mutate state, even if you immediately call setState. Optimized components might not re-render if you do, and the rendering bugs will be tricky to track down.

Instead, always create new objects and arrays when you call setState , which is what we did above with the spread operator. Learn more about how to use the spread operator for immutable updates.

Success! Now check your email. Learn the basics of React in 5 days Finally understand how React works! You will: 🎉 Get something on screen 💄 Write dynamic components 🏃 Make it interactive 😎 Fetch real data 🛳 Put it online 5 days, 5 emails. Walk away with the basics and a plan! Get Lesson 1 right now 👇 There was an error submitting your subscription. Please try again. Email Address I respect your email privacy. Unsubscribe any time. Before we go on, I have to ask... consent

There was an error submitting your subscription. Please try again.