Home >Web Front-end >JS Tutorial >How to Implement Memoization in React to Improve Performance

How to Implement Memoization in React to Improve Performance

Joseph Gordon-Levitt
Joseph Gordon-LevittOriginal
2025-02-09 09:00:15906browse

How to Implement Memoization in React to Improve Performance

This tutorial will explain how to implement Memoization in React. Memorization improves performance by storing the results of expensive function calls and returning these cached results when needed again.

We will cover the following:

  • How to render UI with React
  • Why do you need React memory?
  • How to implement memory for functional and class components
  • Precautions about memory

This article assumes that you have a basic understanding of class components and functional components in React. If you want to review these topics, check out the official React components and props documentation.

How to Implement Memoization in React to Improve Performance

Key Points

  • Memorization in React improves performance by storing the results of expensive function calls and returning these cached results when needed again.
  • React uses virtual DOM to perform DOM updates efficiently, but for large components, the performance impact of checking virtual DOM can be very high. Memorization can help avoid unnecessary re-rendering and virtual DOM checking.
  • React.PureComponent and React.memo() can be used to implement memory in class components and functional components, respectively. These methods prevent unnecessary re-rendering if the component's props or state has not changed.
  • If a function is passed as a prop to a child component, the child component will be re-rendered even if React.memo() is used. To avoid this, you can use the useCallback() hook to prevent recreating the function every time the parent component renders.
  • Memorization should be used with caution in React applications. It works best when a component returns the same output to the same props, contains multiple UI elements (virtual DOM checks affect performance), or often provides the same props.

How to render UI with React

Before going into the detailed introduction of memory in React, let's first look at how React renders the UI using virtual DOM.

The regular DOM basically contains a set of nodes represented as a tree. Each node in the DOM is a representation of the UI element. Whenever a state change occurs in the application, the corresponding nodes of that UI element and all its child elements are updated in the DOM and then the UI is repainted to reflect the updated changes.

Using efficient tree algorithms, node updates are faster, but re-drawing is slower, and when the DOM has a large number of UI elements, it will have an impact on performance. Therefore, a virtual DOM is introduced in React.

This is a virtual representation of the real DOM. Now, whenever the state of the application changes anyway, React does not directly update the real DOM, but creates a new virtual DOM. React then compares this new virtual DOM with the previously created virtual DOM to find the differences that need to be redrawn.

Using these differences, the virtual DOM will effectively update the real DOM with changes. This improves performance because the virtual DOM does not simply update the UI elements and all its child elements, but effectively updates only the minimum necessary changes in the real DOM.

Why do we need React memory

In the previous section, we saw how React can effectively perform DOM updates using virtual DOM to improve performance. In this section, we will look at a use case that explains why memory is needed to further improve performance.

We will create a parent class with a button to increment the state variable named count. The parent component also calls the child component and passes the prop to it. We also added the console.log() statement to the render method of two classes:

<code class="language-javascript">//Parent.js
class Parent extends React.Component {
  constructor(props) {
    super(props);
    this.state = { count: 0 };
  }

  handleClick = () => {
    this.setState((prevState) => {
      return { count: prevState.count + 1 };
    });
  };

  render() {
    console.log("Parent render");
    return (
      <div classname="App">
        <button onclick="{this.handleClick}">Increment</button>
        <h2>Count: {this.state.count}</h2>
        <child name='{"joe"}'></child>
      </div>
    );
  }
}

export default Parent;</code>

The full code for this example is available on CodeSandbox.

We will create a Child class that takes the prop passed by the parent component and displays it in the UI:

<code class="language-javascript">//Child.js
class Child extends React.Component {
  render() {
    console.log("Child render");
    return (
      <div>
        <h2>Name: {this.props.name}</h2>
      </div>
    );
  }
}

export default Child;</code>

Whenever we click the button in the parent component, the count value changes. Since this is a state change, the render method of the parent component is called.

Props passed to subclasses remain unchanged every time the parent re-renders, so the child components should not be re-rendered. However, when we run the above code and continue incrementing the count, we get the following output:

<code>Parent render
Child render
Parent render
Child render
Parent render
Child render</code>

You can increment the count of the above example yourself in the following sandbox and view the output of the console:

[The CodeSandbox link should be embedded here, but since I can't access external websites, it cannot be provided]

From this output we can see that when the parent component re-renders, it also re-renders the child component - even if the props passed to the child component have not changed. This will cause the subcomponent's virtual DOM to perform a difference check with the previous virtual DOM. Since there is no difference in the child component - because props are the same in all re-renders - the real DOM is not updated.

We do have the performance advantage of not updating the real DOM unnecessarily, but we can see here that a new virtual DOM is created and a difference check is performed even if the child components have not actually changed. For small React components, this performance is negligible, but for large components, performance impact is great. To avoid this re-rendering and virtual DOM checking, we use memory.

Memorization in React

In the context of a React application, memorization is a technique where whenever the parent component re-renders, the child component will re-render only when props change. If props have not changed, it does not execute the render method, but returns the cached result. Since the render method is not executed, no virtual DOM and differential checks are created - thus improving performance.

Now let's see how memorization is implemented in classes and functional React components to avoid this unnecessary re-rendering.

(The following content is similar to the original text, except that the language and expression have been adjusted a little, and the image location and format are kept unchanged. I cannot provide a CodeSandbox link due to the inability to access external websites.)

Implement memory in class components

To implement memory in class components, we will use React.PureComponent. React.PureComponent implements shouldComponentUpdate(), which makes shallow comparisons between state and props and renders React components only if props or state changes.

Change the child component to the code shown below:

<code class="language-javascript">//Parent.js
class Parent extends React.Component {
  constructor(props) {
    super(props);
    this.state = { count: 0 };
  }

  handleClick = () => {
    this.setState((prevState) => {
      return { count: prevState.count + 1 };
    });
  };

  render() {
    console.log("Parent render");
    return (
      <div classname="App">
        <button onclick="{this.handleClick}">Increment</button>
        <h2>Count: {this.state.count}</h2>
        <child name='{"joe"}'></child>
      </div>
    );
  }
}

export default Parent;</code>

The full code for this example is as follows: [CodeSandbox link should be embedded here]

The parent component remains unchanged. Now, when we increment count in the parent component, the output in the console is as follows:

<code class="language-javascript">//Child.js
class Child extends React.Component {
  render() {
    console.log("Child render");
    return (
      <div>
        <h2>Name: {this.props.name}</h2>
      </div>
    );
  }
}

export default Child;</code>

For the first rendering, it calls the render method of the parent and child components.

In each incremental subsequent re-render, only the render function of the parent component is called. The child components will not be rerendered.

Implement memory in functional components

To implement memory in a functional React component, we will use React.memo(). React.memo() is a high-order component (HOC) that does similar work to PureComponent to avoid unnecessary re-rendering.

The following is the code for functional components:

<code>Parent render
Child render
Parent render
Child render
Parent render
Child render</code>

We also convert the parent component to a functional component as shown below:

<code class="language-javascript">//Child.js
class Child extends React.PureComponent { // 将React.Component更改为React.PureComponent
  render() {
    console.log("Child render");
    return (
      <div>
        <h2>Name: {this.props.name}</h2>
      </div>
    );
  }
}

export default Child;</code>

The full code for this example can be seen in the following sandbox: [CodeSandbox link should be embedded here]

Now, when we increment count in the parent component, the console outputs the following:

<code>Parent render
Child render
Parent render
Parent render</code>

React.memo() Problems with function prop

In the example above, we see that when we use React.memo() HOC for our child components, even if the parent component is re-rendered, the child components are not re-rendered.

However, a small problem to note is that if we pass the function as a prop to the child component, the child component will be re-rendered even if we use React.memo(). Let's look at an example.

We will change the parent component as shown below. Here we add a handler function that we pass to the child component as props:

<code class="language-javascript">//Child.js
export function Child(props) {
  console.log("Child render");
  return (
    <div>
      <h2>Name: {props.name}</h2>
    </div>
  );
}

export default React.memo(Child); // 在此处为子组件添加HOC以进行记忆化</code>

The subcomponent code remains unchanged. We are not using a function passed as props in a child component:

<code class="language-javascript">//Parent.js
import React, { useState } from 'react';
import Child from './Child';

export default function Parent() {
  const [count, setCount] = useState(0);
  const handleClick = () => {
    setCount(count + 1);
  };
  console.log("Parent render");
  return (
    <div>
      <button onclick="{handleClick}">Increment</button>
      <h2>Count: {count}</h2>
      <child name='{"joe"}'></child>
    </div>
  );
}</code>

Now, when we increment count in the parent component, it re-renders and re-renders the child component even if props have not changed.

So, what caused the child components to re-render? The answer is that every time the parent component re-renders, a new handler function is created and passed to the child component. Now, since the handler function is recreated every time it is rerendered, the child component will find that the handler reference has been changed when it is a shallow comparison of props and rerenders the child component.

In the next section, we will see how to resolve this issue.

useCallback() to avoid further rerendering

The main problem that causes the child component to be re-rendered is the re-creation of the program function, which changes the references passed to the child component. Therefore, we need a way to avoid this recreation. If the handler is not recreated, the reference to the handler will not change - so the child components will not be rerendered.

To avoid recreating the function every time the parent component renders, we will use a React hook called useCallback(). Hooks were introduced in React 16. To learn more about hooks, you can check out React’s official hook documentation or check out “React Hooks: How to Get Started and Build Your Own.”

useCallback()Hook accepts two parameters: callback function and dependency list.

Consider the following useCallback() Example:

<code class="language-javascript">//Parent.js
class Parent extends React.Component {
  constructor(props) {
    super(props);
    this.state = { count: 0 };
  }

  handleClick = () => {
    this.setState((prevState) => {
      return { count: prevState.count + 1 };
    });
  };

  render() {
    console.log("Parent render");
    return (
      <div classname="App">
        <button onclick="{this.handleClick}">Increment</button>
        <h2>Count: {this.state.count}</h2>
        <child name='{"joe"}'></child>
      </div>
    );
  }
}

export default Parent;</code>

Here, useCallback() is added to the handleClick() function. The second parameter [x,y] can be an empty array, a dependency, or a dependency list. The handleClick() function is recreated whenever any dependencies mentioned in the second parameter change.

If the dependency mentioned in useCallback() has not changed, the memorized version of the callback function (as the first parameter) is returned. We will change our parent functional component to use the useCallback() hook for handlers passed to the child component:

(This is similar to the original text, except that the language and expression are adjusted a little, and the image position and format are kept unchanged. I cannot provide a CodeSandbox link due to the inability to access external websites.)

Precautions

Memorization is a good technique to improve React application performance by avoiding unnecessary re-rendering of components when the component's props or state has not changed. You might think of adding memorization to just all components, but this is not a good way to build React components. You should use memory only if the component meets the following conditions:

  • Returns the same output when given the same props
  • Has multiple UI elements and virtual DOM checking affects performance
  • The same props are often provided

Summary

In this tutorial, we have seen:

  • How to render UI with React
  • Why do I need to remember
  • How to achieve memory in React by React.memo() for functional React components and React.PureComponent as class components
  • React.memo()A use case, even after using
  • , the child component will be rerendered
  • useCallback()How to use
  • hook to avoid re-rendering when passing a function as props to a child component.

I hope you find this introduction to React memory useful!

FAQs about React Memorization

(This is similar to the original text, but some adjustments have been made to the language and expression.)

The above is the detailed content of How to Implement Memoization in React to Improve Performance. For more information, please follow other related articles on the PHP Chinese website!

Statement:
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn