ReactJS in Simple English — state (2), shouldComponentUpdate(), PureComponent, contextAPI
By: Johnny Lai
Tags: Reactjs, Learning To Code, React, JavaScript, ES6

This time we would go a little bit deeper about state, including shouldComponentUpdate(), PureComponent and Context API

Before we continue, let’s have a look How ReactJS update our pages

Detected changes in props or state Trigger shouldComponentUpdate(), by default return true, that means it always go to the next step render() render() is called to render the new virtual DOM Compare the old virtual DOM with the newly rendered virtual DOM Update the real DOM if any difference found, only update the parts which are different instead of the whole DOM

By default, if any changes in props or state, React will always render() the new virtual DOM including all components and compare it with the last version.

For example, user just click the submit button and trigger the modal panel and confirm dialog. Obviously, this is a small changes in screen, probably a boolean in state indicated the confirm dialog have to be shown, nothing related with other components.

However, React will still render a virtual DOM including all components and check against with the old version. Think about if we have a large application and every single change will trigger the same behavior - render and check again all components. It would spend a lot of resources and slowdown the application.

In order to enhance the performance, we can implement some checking in shouldComponentUpdate() or apply PureComponent in order to skip some unnecessary render() and speed up the checking (as the new rendered DOM will including less components)

shouldComponentUpdate()

It is one of the lifecycle hooks in React, by default return true, that means it always go to the next step render(). We can implement some checking here, so if the state / props didn’t changed in that class, then we don’t include in the coming rendering.

Let’s the example for better understanding. In this example, we have 3 class. App contains PureInventory and Inventory

App class initialize the state with three variables — counters for App/ PureInventory/ Inventory and the functions to add the counters for App/ PureInventory/ Inventory, and we pass them through props

In the render method, it includes both children — PureInventory and Inventory, and a clock — whenever the render trigger, the clock updates

//App.js class App extends Component { state = {

counter: 0,

childCounter: 0,

pureChildCounter: 0

}; increaseCounterHandler = () => {

this.setState((state) => {

return { counter: state.counter + 1 };

});

}; increaseChildCounterHandler = () => {

this.setState((state) => {

return { childCounter: state.childCounter + 1 };

});

}; increasePureChildCounterHandler = () => {

this.setState((state) => {

return { pureChildCounter: state.pureChildCounter + 1 };

});

}; render() {

console.log("parent render");

return (

<div className="App">

<button onClick={this.increaseCounterHandler}>

Increase App Counter

</button>

<p>App Counter {this.state.counter}</p>

<p>Last Update {new Date().toLocaleString()}</p>

<div>

<Inventory

clicked={this.increaseChildCounterHandler} childCounter= {this.state.childCounter}/>

<PureInventory

clicked={this.increasePureChildCounterHandler} pureChildCounter={this.state.pureChildCounter}/>

</div>

</div>

);

}

}

PureInventory class, a very simple class, have a button to increase its counter and a clock — whenever the render trigger, the clock updates

//PureInventory.js class PureInventory extends Component { render() {

console.log("pure children render");

return (

<div>

<button onClick={this.props.clicked}> Increase PureInventory Counter </button>

<p>PureInventory Counter {this.props.pureChildCounter}</p> <p>Last Update {new Date().toLocaleString()}</p>

</div>

);

}

}

Inventory class, similar with PureInventory class, have a button to increase its counter and a clock — whenever the render trigger, the clock updates

class Inventory extends Component { render() {

console.log("children render");

return (

<div>

<button onClick={this.props.clicked}>Increase Inventory Counter</button>

<p>Inventory Counter {this.props.childCounter}</p>

<p>Last Update {new Date().toLocaleString()}</p>

</div>

);

}

}

As mentioned, by default (if you haven’t implement shouldComponentUpdate() / PureComponent), any changes in the state will trigger the render() (of all class)to re-render everything again in the new virtual DOM

App counter increase, both child — Inventory and PureInventory re-render too, as the time for last update are same

Inventory counter increase, both Parent and PureInventory re-render too, as the time for last update are same

Pure Child counter increase, both Parent and Child re-render too, as the time for last update are same

In order to fix this, we can implement shouldComponentUpdate(nextProps, nextState) in PureInventory Class - nextProps means the changed props, nextState means the changed state.

So, we can compare the current props/state with the changes props/ state.

In this case, PureInventory only got 1 props — pureChildCounter and we compare the new props and old props by !==, that means only go further (render()) if the new pureChildCounter is different from the current pureChildCounter.

//PureInventory.js class PureInventory extends Component { shouldComponentUpdate(nextProps, nextState) {

return

this.props.pureChildCounter !== nextProps.pureChildCounter;

} render() {

console.log("pure children render");

return (

<div>

<button onClick={this.props.clicked}> Increase Pure Child Counter </button>

<p>Pure Child Counter {this.props.pureChildCounter}</p> <p>Last Update {new Date().toLocaleString()}</p>

</div>

);

}

}

After implemented shouldComponentUpdate() in PureInventory Class, it no longer re-render again if the increase of counter is in App or Inventory (not related with PureInventory)

Let try the working example