Development

React: Optimize Components with React.memo, useMemo, and useCallback

Learn how to optimize components in React.memo, useMemo, and useCallback. Occasionally you can run into scenarios where your components are rendering more often than they need to and slowing your site down.

3 min
June 3, 2019
Chris Held
Development Lead

In most cases, React performance is not something you need to worry about. The core library does a ton of work under the hood to make sure everything is rendering efficiently. However, occasionally you can run into scenarios where your components are rendering more often than they need to and slowing your site down.

**Let's look at an example:**

   -- CODE line-numbers language-jsx --
   <!--
   const ListPage = ({data, title}) => (
   <div>
     <Header title={title}/>
     <List listItems={data}/>
   </div>
   )
   -->

In this example, any changes to `data` will cause `ListPage` to re-render all of it's child components, including the `Header` component, even if `title` didn't change. The `Header` will render the same result given the same props, so any render with the same props is not necessary. In this case it's probably not a big deal, but if `<Header/>` was performing some expensive computation every time it rendered we would want to make sure it was only rendering when necessary.

**Thankfully, there are a few ways to optimize for this scenario.**

When using class based components, `PureComponent` will return the last rendered value if the passed in props are the same. There is also a `shouldComponentUpdate` function for more fine tuned control. When using functional components, React provides three methods of optimization that this article will be focusing on: `React.memo`, `useMemo`, and `useCallback`.

two men discussing react project on computer screen

## React.Memo

`React.memo` is a higher order component that memoizes the result of a function component. If a component returns the same result given the same props, wrapping it in `memo` can result in a performance boost. Take our `<Header/>` example earlier.

**Let's say it looks something like this:**

   -- CODE line-numbers language-jsx --
   <!--
   const Header = ({title}) => <h1>{title}</h1>

   export default Header;
   -->

We can see that this component isn't going to need to be rendered unless `title` changes, so it would be safe to wrap it in `React.memo`.

   -- CODE line-numbers language-jsx --
   <!--
   const Header = ({title}) => <h1>{title}</h1>

   export default React.memo(Header);
   -->

Now, whenever `Header` is rendered, it will do a shallow comparison on it's props. If those props are the same, it will skip rendering and instead return it's last rendered value.

A quick note about using `memo` and react dev tools. At the time of this writing, wrapping your component like this...

   -- CODE line-numbers language-jsx --
   <!--
   const Header = React.memo(({title}) => <h1>{title}</h1>));

   export default Header;
   -->

...will cause your component to show up as `Unknown` in react dev tools. To fix this, wrap your component in `memo` after defining it, like we did previously:

   -- CODE line-numbers language-jsx --
   <!--
   const Header = ({title}) => <h1>{title}</h1>;

   export default React.memo(Header);
   -->

## useMemo

`useMemo` allows you to memoize the results of a function, and will return that result until an array of dependencies change.

**For example:**

   -- CODE line-numbers language-js --
   <!--
   const widgetList = useMemo(
     () => widgets.map(
       w => ({
         ...w,
         totalPrice: someComplexFunction(w.price),
         estimatedDeliveryDate: someOtherComplexFunction(w.warehouseAddress)
       }),
     ),
     [widgets],
   );
   -->

In this example, a component receives a list of widgets. The widgets coming into the component need to be mapped to include total price and an estimated delivery date, which uses some kind of complex and expensive function. If this component renders and the value of `widgets` is the same, there is no need to run those expensive functions again.

Using `useMemo` will memoize the result, so if `widgets` haven't changed since the component's last render it will skip the function call and return what it got last.

## useCallback

`useCallback` can prevent unnecessary renders between parent and child components.

**Take this example:**

   -- CODE line-numbers language-js --
   <!--
   const Parent = () => {
     const [showExtraDetails, setShowExtraDetails] = useState(false);
     return (
       [...]
       <Child onClick={() => { showData(showExtraDetails); }/>
       [...]
     );
   }
   -->

This component will cause `Child` to re-render every time `Parent` does, even if `Child` is a `PureComponent` or wrapped in `React.memo`, because the `onClick` will be different every render. `useCallback` can handle this situation like so:

   -- CODE line-numbers language-js --
   <!--
   const Parent = () => {
     const [showExtraDetails, setShowExtraDetails] = useState(false);
     const handleClick = useCallback(
       () => {
         showData(showExtraDetails);
       },
       [showExtraDetails],
     );
     return (
       [...]
       <Child onClick={() => {handleClick}/>
       [...]
     );
   }
   -->

Now `handleClick` will have the same value until `showExtraDetails` changes, which will reduce the number of times `Child` renders.

## Things to consider with optimization in React

A word of caution around premature optimization. React is typically fast enough to handle most use cases without resorting to any of these techniques. I would advise you to build your components without any optimization first, and look into adding performance enhancements only when necessary.

## Resources to learn more

If you'd like to explore these APIs further, here are some resources to help give you a better understanding.

[React.memo](https://reactjs.org/docs/react-api.html#reactmemo](https://reactjs.org/docs/react-api.html#reactmemo)

[useMemo](https://reactjs.org/docs/hooks-reference.html#usememo](https://reactjs.org/docs/hooks-reference.html#usememo)

[useCallback](https://reactjs.org/docs/hooks-reference.html#usecallback](https://reactjs.org/docs/hooks-reference.html#usecallback)

If you're looking to further optimize your React application, the react docs contain a [great section on performance.](https://reactjs.org/docs/optimizing-performance.html](https://reactjs.org/docs/optimizing-performance.html)

## Want to join our development team?

We're always looking for talented and open-minded people to help us bring new ideas to life. See what it's like to work at Headway and the opportunities we currently have available.

[Learn more about careers at Headway](https://www.headway.io/careers)

Actionable UX audit kit

  • Guide with Checklist
  • UX Audit Template for Figma
  • UX Audit Report Template for Figma
  • Walkthrough Video
By filling out this form you agree to receive our super helpful design newsletter and announcements from the Headway design crew.

Create better products in just 10 minutes per week

Learn how to launch and grow products less chaos.

See what our crew shares inside our private slack channels to stay on top of industry trends.

By filling out this form you agree to receive a super helpful weekly newsletter and announcements from the Headway crew.