Improving React Component Functionality with React.memo ()

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



The React.js team is working hard to make React run as fast as possible. To enable developers to speed up their React applications, the following tools have been added to it:

  • React.lazy and Suspense for delayed component loading;
  • Pure component
  • lifecycle hooks shouldComponentUpdate (...) {...}.

In this article, we will consider, among others, another optimization tool added in React v16.6 to speed up 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 things up. Give it a try!



Extra render


In React, each component corresponds to a view unit. Components also have states. When the state value changes due to user actions, the component realizes that redrawing is needed. The React component can be redrawn any number of times. In some cases, this is necessary, but most often you can do without a renderer, especially since it greatly slows down the application.

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 (
            
{this.state.count}
); } } export default TestC;

The initial value of the {count: 0} state is 0. If you click the Click me button, the count state will become 1. On our screen, 0 will also change to 1. But if we click the button again, problems begin: the component should not be redrawn, because it the condition has not changed. The counter value “to” is 1, the new value is also one, which means that there is no need to update the DOM.

To see the update of our TestC, in which the same state is set twice, I added two life cycle methods. React starts the componentWillUpdate cycle when the component is updated / redrawn due to a state change. The componentdidUpdate React cycle starts when a component renders successfully.

If we launch the component in the browser and try to click the Click me button several times, we 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


The shouldComponentUpdate lifecycle hook will help avoid unnecessary rendering in React components.

React launches the shouldComponentUpdate method at the beginning of component rendering and receives a green light from this method to continue the process or a signal that the process is inhibited .

Let our shouldComponentUpdate look like this:

shouldComponentUpdate(nextProps, nextState) {
        return true        
    }

  • nextProps: the next value propsthat the component will receive;
  • nextState: The next value statethat the component will receive.

So we allow React to render the component because the return value true.

Suppose we write the following:

shouldComponentUpdate(nextProps, nextState) {
        return false
    }

In this case, we forbid React to render the component, because the value is returned false.
From the above it follows that for rendering the component we need the value 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 ( 
            
{ this.state.count }
); } } export default TestC;

We added a shouldComponentUpdate hook to the TestC component. Now the value countin the object of the current state is this.state.countcompared with the value countin the object of the next state nextState.count. If they are equal ===, redrawing does not occur and the value is returned false. If they are not equal, a value is returned trueand a renderer is launched to display the new value.

If we test the code in a browser, we will see a familiar result:



But by clicking on the button Click Meseveral times, all we see will be the following (displayed only once!): You can change the state 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:

componentWillUpdate
componentDidUpdate








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 redraw was required.
Let's move on to the Pure Component .

Pure Component appeared in React in version v15.5. It is used to compare default values ​​( change detection). Using extend React.PureComponent, you can not add a life cycle method shouldComponentUpdateto components: change tracking happens by itself.

Add a 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 ( 
            
{ this.state.count }
); } } export default TestC;

As you can see, we made shouldComponentUpdatea comment. We no longer need it: it does all the work React.PureComponent.

Rebooting the browser to test the new solution, and clicking on the button Click Meseveral times, we get:





As you can see, only one entry appeared in the console component*Update.

After seeing how to work in React with redrawing in the component classes of ES6, we move on to the component functions. How to achieve the same results with them?

Function Components


We already know how to optimize work with classes using the Pure Component and the life cycle method shouldComponentUpdate. No one argues that class components are the main components of React, but you can use functions as components.

function TestC(props) {
    return (
        
I am a functional component
) }

It is important to remember that component functions, unlike component classes, have no state (although now that hooks have appeared useState, this can be argued with), which means that we cannot configure 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 a method shouldComponentUpdateto tell React that a function renderer is needed. (Perhaps, in the last sentence, the author made a factual mistake. - Approx. Ed.) And, of course, we cannot use it extend React.PureComponent.

We turn our component class ES6 TestC into a component function.

import React from 'react';
const TestC = (props) => {
    console.log(`Rendering TestC :` props)
    return ( 
        
{props.count}
) } export default TestC; // App.js

After rendering in the console, we see a record Rendering TestC :5.



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 numerical value and press Enter, the properties of the component will change according to the value we entered. Suppose at 45.



Go to the Console tab.



The TestC component was redrawn because the previous value of 5 changed to the current one - 45. Go back to the React tab and change the value to 45, then go back to Console.



As you can see, the component is redrawn again, although the previous and new values ​​are the same. :(

How to manage the renderer?

Solution: React.memo ()


React.memo()- New in React v16.6. The principle of its operation is similar to the principle of work React.PureComponent: assistance in managing the redrawing of component functions. React.memo(...)for class components, this is React.PureComponentfor function components.

How to work with React.memo (...)?
Pretty simple. Say we have a component function.

const Funcomponent = ()=> {
    return (
        
Hiya!! I am a Funtional component
) }

We only need to pass FuncComponent as an argument to the React.memo function.

const Funcomponent = ()=> {
    return (
        
Hiya!! I am a Funtional component
) } const MemodFuncComponent = React.memo(FunComponent)

React.memo returns purified MemodFuncComponent. This is what we will draw in the JSX markup. When the properties and state of a component change, React compares the previous and current properties and states of the component. And only if they are not identical, the component function is redrawn.

Apply this to the TestC function component.

let TestC = (props) => {
    console.log('Rendering TestC :', props)
    return ( 
        
{ props.count } ) } TestC = React.memo(TestC);

Open a browser and download the application. Open DevTools and go to the React tab. Select .

If in the block on the right we change the properties of the counter to 89, the application will be redrawn.



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



There will be no redrawing!

Glory to React.memo (...)! :)

Without application React.memo(...)in our first example, the TestC component function is redrawn even when the previous value changes to the identical one. Now, thanks React.memo(...), we can avoid unnecessary rendering of component-functions.

Conclusion


  • Let's go through the list?
  • React.PureComponent - silver;
  • React.memo(...) - gold;
  • React.PureComponent works with ES6 classes;
  • React.memo(...) works with functions;
  • React.PureComponent optimizes the redrawing of ES6 classes;
  • React.memo(...) optimizes redrawing functions;
  • feature optimization is a terrific idea;
  • React will never be the same again.

If you have any questions about the article or any additional information, changes or objections, do not hesitate to write me comments, emails or private messages.

Thanks!

Also popular now: