📜 ⬆️ ⬇️

Increase performance in React feature components with React.memo ()

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:
')

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 } 


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') } /*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; 

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; // App.js <TestC count={5} /> 

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



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!

Source: https://habr.com/ru/post/442116/


All Articles