React 16 lets you decide whether state gets updated via .setState
to prevent unnecessary DOM updates. Calling .setState
with null
no longer triggers an update in React 16.
We’ll explore how this works by refactoring a mocktail selection app that updates even if we choose the same mocktail twice.
The folder structure might look like this:
src |-> App.js |-> Mocktail.js |-> index.js |-> index.css |-> Spinner.js
Our application will render a selected mocktail. We can select/switch the mocktail by clicking on one of the buttons. When we do that, a new mocktail is loaded, and a new mocktail image is rendered after loading is complete.
The App
component — the parent component — has a mocktail
state and an updateMocktail
method that handles updating the mocktail.
import React, { Component } from 'react'; import Mocktail from './Mocktail'; class App extends Component { state = { mocktail: '' } updateMocktail = mocktail => this.setState({ mocktail }) render() { const mocktails = ['Cosmopolitan', 'Mojito', 'Blue Lagoon']; return ( <React.Fragment> <header> <h1>Select Your Mocktail</h1> <nav> { mocktails.map((mocktail) => { return <button key={mocktail} value={mocktail} type="button" onClick={e => this.updateMocktail(e.target.value)}>{mocktail}</button> }) } </nav> </header> <main> <Mocktail mocktail={this.state.mocktail} /> </main> </React.Fragment> ); } } export default App;
The updateMocktail
method is called on the button
element’s onClick
event, and the mocktail
state is being passed down to the child component Mocktail
.
The Mocktail
component has a loading state called isLoading
that, when true, renders the Spinner
component.
import React, { Component } from 'react'; import Spinner from './Spinner'; class Mocktail extends Component { state = { isLoading: false } componentWillReceiveProps() { this.setState({ isLoading: true }); setTimeout(() => this.setState({ isLoading: false }), 500); } render() { if (this.state.isLoading) { return <Spinner/> } return ( <React.Fragment> <div className="mocktail-image"> <img src={`img/${this.props.mocktail.replace(/ +/g, "").toLowerCase()}.png`} alt={this.props.mocktail} /> </div> </React.Fragment> ); } } export default Mocktail;
setTimeout
is called in the Mocktail
component’s componentWillReceiveProps
lifecycle method to set the loading state to true
for 500 milliseconds.
This displays the loading spinner for half a second each time the Mocktail
component’s props get updated with the new mocktail
state, then it renders the mocktail image.
Now, the problem with this is that the mocktail
state gets updated and triggers a re-render of the Mocktail
component no matter what — even if the state doesn’t actually change.
For example, each time I click the Mojito button, we see the app unnecessarily re-render the Mojito image. React 16 provides state performance improvements that enable us to prevent an update from being triggered by returning null
in setState
if the state’s new value is the same as its existing value.
Here are the steps we will follow to prevent unnecessary re-render:
null
null
will not update state and trigger a component re-renderSo first, in the updateMocktail
method of the App
component, we’ll create a constant called newMocktail
and assign it the value being passed in for the mocktail
.
updateMocktail = mocktail => { const newMocktail = mocktail; this.setState({ mocktail }) }
Since we’re going to be checking and setting state based on a previous state, rather than passing setState
and object
, we’ll pass it a function that takes the previous state as a parameter. Then we’ll check if the new value of the mocktail
state is the same as the existing one.
If the values are the same, setState
will return null
. Otherwise, if the values are different, setState
will return the updated mocktail
state, which will trigger a re-render of the Mocktail
component with the new state.
updateMocktail = mocktail => { const newMocktail = mocktail; this.setState(state => { if (state.mocktail === newMocktail) { return null; } else { return { mocktail }; } }) }
Now, clicking a button still loads its respective mocktail image. However, if we click the button again for the same mocktail, React does not re-render the Mocktail
component; because setState
is returning null
, there is no state change to trigger an update.
I’ve highlighted the updates in React DevTools in the two gifs below:
Note: I have used a dark theme here so it is easier to observe the update in React DOM using the React DevTools highlight updates feature.
Now we have covered returning null from setState
in React 16. I’ve added the full code for the mocktail selection app in the CodeSandbox below for you to play around with and fork.
Preventing unnecessary state updates and re-renders with null
can make our application perform faster, and the whole point of making application perform faster is to improve our app’s user experience.
Users don’t stumble on a product for no reason. How users feel about a product directly reflects their views of the company and its products, so we need to make sure we build an experience around our users’ expectations in a way that feels natural and intuitive.
I hope you’ve found this post informative and helpful. I would love to hear your feedback!
Thank you for reading!
Install LogRocket via npm or script tag. LogRocket.init()
must be called client-side, not
server-side
$ npm i --save logrocket // Code: import LogRocket from 'logrocket'; LogRocket.init('app/id');
// Add to your HTML: <script src="https://cdn.lr-ingest.com/LogRocket.min.js"></script> <script>window.LogRocket && window.LogRocket.init('app/id');</script>
Hey there, want to help make our blog better?
Join LogRocket’s Content Advisory Board. You’ll help inform the type of content we create and get access to exclusive meetups, social accreditation, and swag.
Sign up nowDesign React Native UIs that look great on any device by using adaptive layouts, responsive scaling, and platform-specific tools.
Angular’s two-way data binding has evolved with signals, offering improved performance, simpler syntax, and better type inference.
Fix sticky positioning issues in CSS, from missing offsets to overflow conflicts in flex, grid, and container height constraints.
From basic syntax and advanced techniques to practical applications and error handling, here’s how to use node-cron.
3 Replies to "Returning null from setState in React 16"
Point of clarification for someone relatively new to React: am I correct to say that it is not necessary to return null with setstate in the way described in this article when the [unchanging] state value(s) are members of a mapped list (where each member in the list has been assigned a unique key)? Or restated, is it the case that a mapped list, where each element in the list is assigned a unique key, only updates those elements where a state change has occurred, and so the technique explained by this article would not be necessary for unchanging elements of a mapped list even when other elements in the list undergo state changes?
Great article, by the way. Everything makes sense. My question is mostly to make sure I have everything straight when it comes to state updates.
Why not just completely skip the setState call if new === current mocktail?
Yeah