/ React

How React Determines Re-rendering Of A Component

React is a library for building UI interfaces best known for its fast rendering performance. Its speed is made possible by its use of a virtual DOM (Document Object Model), a virtual representation of HTML in memory. This virtual DOM is used to render a virtual HTML tree first, and when a component state changes, instead of writing the whole HTML tree to the DOM, React will only write the difference between the new tree and the previous tree, since React has both trees in its memory.

For every state change, React's default behavior is to re-render a component even when we are displaying the same information. It is up to you as developer to be aware of this behavior so you can prevent React from rendering unnecessarily. Understanding this behavior in React can help you improve the performance of your application.

When A Component State Changes

In React, state and props has one major difference -- the state can be changed while props are fixed values. Class-based components can only change their internal state but not their properties.

A re-render of a component is triggered when its state has changed. This can occur when props has changed or when you directly update the state using the setState call. The component will receive the updated state, and it will decide if it should re-render the component.

However, and here's the important part -- React's default behavior is to always re-render a component on any state change, even if we are displaying the same data. This unnecessary re-render can have huge performance impact on your application if not handled properly.

Let us look at the following React component (Note: setInterval was deliberately used for demonstration purposes only):

class TaskItem extends React.Component {

    componentDidMount() {
        setInterval(() => {
            this.setState(() => {
                console.log('Setting state...')
                return { description: "New Task" }
            });
        }, 1000);
    }

    render() {
        console.log('Render called')
        return (<div>TaskItem Component</div>)
    }
}

ReactDOM.render(<TaskItem />, document.getElementById('app'))

In the class component above, TaskItem will re-render every second even if the render method doesn't evaluate the state object, { description: "New Task" } object. You can see a working version of the code in this CodePen

Unnecessary renders is not an ideal behavior in your application. React doesn't know when to ignore parts of the state, so React just re-renders on every state change. Although this behavior is ideal in a vast majority of cases, in our use case, it is not.

So how can we avoid re-rendering of a component? There's a method called shouldComponentUpdate() to do just that.

Using shouldComponentUpdate() method

You can use shouldComponentUpdate() method to tell React if a component's output should not be affected by the current change in state or props.

React's default return value for the shouldComponentUpdate is always true. This is the reason why our component always re-renders every time. By using this method, you can override the method's default value when you prefer to prevent a re-render.

By overriding this method, when React is about to render the component, it will execute shouldComponentUpdate() and check if it returns true (component should re-render) or false (skip re-render this time). You have to implement this method to return true or false to tell React as to whether to re-render or to skip rendering.

Going back to our previous example component, we have to set which data to check for the re-render to happen.

class TaskItem extends React.Component {

    componentDidMount() {
        setInterval(() => {
            this.setState(() => {
                return { description: "New Task" }
            });
        }, 1000);
    }
    
    shouldComponentUpdate(nextProps) {
        const newTask = this.props.taskName !== nextProps.taskName
        const newStatus = this.props.status != nextProps.status
        
        return newTask || newStatus
    }

    render() {
        console.log('Render called')
        return (<div>TaskItem Component</div>)
    }
}

ReactDOM.render(
        <TaskItem 
            taskName="Learn Go" 
            status="In-progress" 
        />, 
        document.getElementById('app'))

As seen in the example above, we only want to re-render the TaskItem component if the taskName or status attributes have changed. We are not interested if the description attribute changed, and so we didn't write any check for it in the shouldComponmentUpdate() method.

By the time React is about to render TaskItem component, when triggered by the setState, it will first check if the state has changed (through props or state). Since we made an explicit call to setState, React will now invoke the shouldComponentUpdate() method of our component. Depending if the result is either true or false, React will decide whether to re-render or not.

In our updated component, setState will still be invoked every second, but the render will only happen once -- in the initial load or when the taskName or status attributes have changed. You can preview the updated component working in this CodePen

Gotchas of shouldComponentUpdate

Returning false does not prevent child components from re-rendering when their state changes. ~ React Documentation

This rule only applies to a child component's state, but not their props. This means that a child component will still re-render if it has a setState of its own. If the parent component returns false from its shouldComponentUpdate() method, it will not pass the updated props to its children, and hence, the child components will not re-render even if their own props gets updated.

Resources