DEV Community

hmintoh
hmintoh

Posted on

How to: Increase performance with React.memo

In React, changes to a component's state triggers a re-render of the entire component tree. This means that not only does the render function of the component get called, all its subsequent child-components will re-render as well, even when their props have not changed.

When your application is large, unnecessary re-rendering is expensive and reduces performance. To avoid this, we can look at to a concept called Memoization.

Memoization

Wikipedia defines Memoization to be:

... an optimisation technique used primarily to speed up computer programs by storing the results of expensive function calls and returning the cached result when the same inputs occur again.

This means that it is a technique that executes a pure function once, stores the results, and if the function is called again with the same arguments, it returns the previously saved results without executing the function again (because we assume the outcome is the same).

In other words, it is a away to lower the function's time cost in exchange for space cost; that is, memoized functions become optimised for speed in exchange for a higher use of computer memory space.

React.memo in action

In the context of React, the functions are our React (pure) components, and the arguments are our props. React.memo is a HOC built of the concept of memoization and tells the component to only re-render when its props change.

Let's create a component that has an input field and a button to add a new user:

const App = () => {
  const [value, setValue] = useState("");
  const [names, setNames] = useState(["Avery", "Bain"]);

  const handleInput = (e) => {
    setValue(e.target.value)
  }

  const handleAddUser = () => {
    const updatedArray = names.push(value);
    setNames(updatedArray);
  }

  console.log('render App');
  return (
    <div>
        <input value={value} onChange={handleInput} />
        <button onClick={handleAddUser}>
          Add User
        </button>
        <List list={names} />
    </div>
  )
};

const List = ({list}) => {
  console.log('render List');
  return (
    <ul>
      {list.map((name, key) => <li>{name}</li>)};
    </ul>
  )
};

export default App;

In here, the component gets re-rendered every time someone types into the input field (before adding a user), because value changes. The problem is, List also gets re-rendered, even when its props remains the same.

// when user types one char into input
render App
render List

With React.memo, we can optimise rendering behaviour by telling List to only re-render when its props changes:

import { memo } from 'React';

const List = memo(({list}) => {
    console.log('render List');
    return (
        <ul>
          {list.map((name, key) => <li>{name}</li>)};
        </ul>
    )
});

Now, when we type into the input field, only App is re-rendered because it is the only component affected by the state change.

// when user types one char into input
render App

// when a new user is added
render App
render List

To recap

By default, React re-renders components when its state changes. This means that a child component will also render as a result of a state change in its parent component, even when its props remain the same.

React's memo API optimises rendering behaviour by re-rendering components only when their props change. Thoughts? Let me know in the comments below! 👇

References

https://www.robinwieruch.de/react-memo

Top comments (2)

Collapse
 
calag4n profile image
calag4n

Hi hmintoh, great post :) react memo is so usefull !
Though, I think you've made a mistake in the first code block :
You wrote:

<List list={users} />

instead of

<List list={names} />

and

const List = ({users}) => {
  console.log('render List');
  return (
    <ul>
      {users.map((user, key) => <li>{user}</li>)};
    </ul>
  )
};

instead of

const List = ({list}) => {
  console.log('render List');
  return (
    <ul>
      {list.map((user, key) => <li>{user}</li>)};
    </ul>
  )
};
Collapse
 
hmintoh profile image
hmintoh • Edited

fixed, thank you for flagging @calag4n !