We present you a translation of the article Chidume Nnamdi, which was published on blog.bitsrc.io. If you want to learn how to avoid unnecessary rendering and how useful new tools in React are, welcome under cat.

The React.js team is working hard to make React work as quickly as possible. So that developers can speed up their applications written in React, the following tools have been added to it:
')
- React.lazy and Suspense for deferred loading of components;
- Pure Component;
- hooks of the life cycle shouldComponentUpdate (...) {...}.
In this article, we will consider, among others, another optimization tool added in React v16.6 for accelerating the component-functions -
React.memo .
Tip: use
Bit to install and share React components. Use your components to build new applications and share them with the team to speed up work. Try it!

Extra render
In React, each component has a unit of view. Components also have states. When the state value changes due to user actions, the component realizes that a redraw is needed. The React component can be redrawn any number of times. In some cases this is necessary, but more often you can do without a renderer, especially since it slows down the application greatly.
Consider the following component:
import React from 'react'; class TestC extends React.Component { constructor(props) { super(props); this.state = { count: 0 } } componentWillUpdate(nextProps, nextState) { console.log('componentWillUpdate') } componentDidUpdate(prevProps, prevState) { console.log('componentDidUpdate') } render() { return ( <div > {this.state.count} <button onClick={()=>this.setState({count: 1})}>Click Me</button> </div> ); } } export default TestC;
The initial state value {count: 0} is 0. If you click on the Click me button, the count state becomes 1. On our screen, 0 will also change to 1. But if we click on the button again, problems start: the component should not be redrawn, because condition has not changed. The value of the “before” counter is 1, the new value is also one, which means there is no need to update the DOM.
To see our TestC update, which sets the same state twice, I added two lifecycle methods. React starts the componentWillUpdate loop when the component is updated / redrawn due to a state change. The cycle componentdidUpdate React starts when the component is successfully rendered.
If you start the component in the browser and try to click on the Click me button several times, we will get the following result:

Repeating the componentWillUpdate entry in our console indicates that the component is redrawn even when the state does not change. This is an extra render.
Pure Component / shouldComponentUpdate
Avoid unnecessary rendering in the React components will help the life cycle hook shouldComponentUpdate.
React runs the
shouldComponentUpdate method at the beginning of the component's drawing and receives the green light from this method to continue the process or a signal to deny the process.
Let our shouldComponentUpdate look like this:
shouldComponentUpdate(nextProps, nextState) { return true }
nextProps
: the next props
value that the component will receive;nextState
: the next state
value that the component will receive.
So we allow React to draw the component, because the return value is
true
.
Suppose we write the following:
shouldComponentUpdate(nextProps, nextState) { return false }
In this case, we prohibit React from rendering the component, because
false
returned.
From the above, it follows that in order to render the component, we need to return
true
. Now we can rewrite the TestC component as follows:
import React from 'react'; class TestC extends React.Component { constructor(props) { super(props); this.state = { count: 0 } } componentWillUpdate(nextProps, nextState) { console.log('componentWillUpdate') } componentDidUpdate(prevProps, prevState) { console.log('componentDidUpdate') } shouldComponentUpdate(nextProps, nextState) { if (this.state.count === nextState.count) { return false } return true } render() { return ( <div> { this.state.count } <button onClick = { () => this.setState({ count: 1 }) }> Click Me </button> </div> ); } } export default TestC;
We added the shouldComponentUpdate hook to the TestC component. Now the
count
value in the current state object
this.state.count
compared with the
count
value in the next state object
nextState.count
. If they are equal
===
, no redrawing occurs and
false
returned. If they are not equal,
true
returned and a re-render is started to display the new value.
If we test the code in the browser, we will see the familiar result:

But pressing the
Click Me
button several times, all that we will see will be the following (displayed only once!):
componentWillUpdate
componentDidUpdate

You can change the status of the TestC component in the React DevTools tab. Click on the React tab, select TestC on the right, and you will see the counter status value:

This value can be changed. Click on the counter text, type 2 and press Enter.

The state of count will change, and in the console we will see:
componentWillUpdate componentDidUpdate componentWillUpdate componentDidUpdate

The previous value was 1, and the new one was 2, so a redrawing was required.
Let's go to the
Pure Component .
Pure Component appeared in React in version v15.5. With it, compare the default values ​​(
change detection
). Using
extend React.PureComponent
, you can not add the life cycle method
shouldComponentUpdate
to the components: change tracking happens by itself.
Add PureComponent to the TestC component.
import React from 'react'; class TestC extends React.PureComponent { constructor(props) { super(props); this.state = { count: 0 } } componentWillUpdate(nextProps, nextState) { console.log('componentWillUpdate') } componentDidUpdate(prevProps, prevState) { console.log('componentDidUpdate') } render() { return ( <div> { this.state.count } <button onClick = { () => this.setState({ count: 1 }) }> Click Me </button> </div > ); } } export default TestC;
As you can see, we brought out
shouldComponentUpdate
to a comment. We don’t need it anymore:
React.PureComponent
does all the work.
Restarting the browser to test the new solution, and clicking on the
Click Me
button several times, we get:


As you can see, only one
component*Update
entry appeared in the console.
Having looked at how to work in React with redrawing in components-classes of ES6, let us turn to components-functions. How to achieve the same results with them?
Component Functions
We already know how to optimize working with classes using the Pure Component and the life cycle method
shouldComponentUpdate
. Nobody argues with the fact that class components are the main components of React, but functions can also be used as components.
function TestC(props) { return ( <div> I am a functional component </div> ) }
It is important to remember that component-functions, unlike component-classes, do not have a state (although now, when
useState
hooks have
useState
, we can argue with this), which means that we cannot customize their redrawing. The life cycle methods that we used while working with classes are not available to us here. If we can add lifecycle hooks to function components, we can add the
shouldComponentUpdate
method to tell React that a function must be rendered. (Perhaps in the last sentence, the author made a factual error. - Appro. Ed.) And, of course, we cannot use
extend React.PureComponent
.
Let's transform our component class ES6 TestC into a component function.
import React from 'react'; const TestC = (props) => { console.log(`Rendering TestC :` props) return ( <div> {props.count} </div> ) } export default TestC;
After drawing in the console, we see the
Rendering TestC :5
entry.

Open DevTools and click on the React tab. Here we will try to change the value of the properties of the TestC component. Select TestC, and the counter properties with all the properties and values ​​of TestC will open on the right. We see only the counter with the current value of 5.
Click on the number 5 to change the value. An input window will appear instead.

If we change the numeric value and press Enter, the properties of the component will change according to the value we entered. Suppose at 45.

Click the Console tab.

The TestC component has been redrawn because the previous value of 5 has changed to the current one - 45. Return to the React tab and change the value to 45, then go to the Console again.

As you can see, the component is redrawn again, although the previous and new values ​​are the same. :(
How to manage rerender?
Solution: React.memo ()
React.memo()
is a new product introduced in React v16.6. The principle of its work is similar to the principle of
React.PureComponent
: help in managing the redrawing of component-functions.
React.memo(...)
for component classes is
React.PureComponent
for component functions.
How to work with React.memo (...)?Pretty simple. Let's say we have a component function.
const Funcomponent = ()=> { return ( <div> Hiya!! I am a Funtional component </div> ) }
We only need to pass FuncComponent as an argument to the React.memo function.
const Funcomponent = ()=> { return ( <div> Hiya!! I am a Funtional component </div> ) } const MemodFuncComponent = React.memo(FunComponent)
React.memo returns a
purified MemodFuncComponent
. This is what we will draw in the JSX markup. When the properties and state of the component change, React compares the previous and current properties and states of the component. And only if they are non-identical, the component function is redrawn.
Apply this to the TestC component functions.
let TestC = (props) => { console.log('Rendering TestC :', props) return ( <div> { props.count } </> ) } TestC = React.memo(TestC);
Open a browser and download the application. Open DevTools and go to the React tab. Select
<Memo(TestC)>
.
If in the block on the right we change the counter properties to 89, the application will be redrawn.

If we change the value to the same as the previous one, 89, then ...

There will be no redrawing!
Glory to React.memo (...)! :)
Without the use of
React.memo(...)
in our first example, the component function TestC is redrawn even when the previous value changes to the same. Now, thanks to
React.memo(...)
, we can avoid unnecessary rendering of component-functions.
Conclusion
- Go through the list?
React.PureComponent
- silver;React.memo(...)
- gold;React.PureComponent
works with ES6 classes;React.memo(...)
works with functions;React.PureComponent
optimizes redrawing of ES6 classes;React.memo(...)
optimizes function redrawing;- function optimization is a great idea;
React
will never be the same again.
If you have any questions about the article or any additional information, edits or objections, do not hesitate to write
me comments, emails or personal messages.
Thank!