Abishek Jakhar Designer, Developer & Gamer

Returning null from setState in React 16

3 min read 912

Returning Null From setState In React 16

Overview

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.

Our mocktail selection app.

The folder structure might look like this:

src
 |-> App.js
 |-> Mocktail.js
 |-> index.js
 |-> index.css
 |-> Spinner.js

How our application works

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.

The problem

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.

The solution

Here are the steps we will follow to prevent unnecessary re-render:

  1. Check whether the new value of the state is the same as the existing value
  2. If the values are the same, we will return null
  3. Returning null will not update state and trigger a component re-render

So 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:

Before Returning Null From setState
After Returning Null From setState
Before returning null (above) vs. after returning null (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.

Conclusion

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!

Plug: , a DVR for web apps

LogRocket is a frontend application monitoring solution that lets you replay problems as if they happened in your own browser. Instead of guessing why errors happen, or asking users for screenshots and log dumps, LogRocket lets you replay the session to quickly understand what went wrong. It works perfectly with any app, regardless of framework, and has plugins to log additional context from Redux, Vuex, and @ngrx/store.

In addition to logging Redux actions and state, LogRocket records console logs, JavaScript errors, stacktraces, network requests/responses with headers + bodies, browser metadata, and custom logs. It also instruments the DOM to record the HTML and CSS on the page, recreating pixel-perfect videos of even the most complex single-page apps.

.

Abishek Jakhar Designer, Developer & Gamer

3 Replies to “Returning null from setState in React 16”

  1. 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.

Leave a Reply