← Back to index
PUBLISHED
#0063

memo

CreatedOct 23 2018
UpdatedOct 23 2018
  • Start Date: 2018-10-18
  • RFC PR: https://github.com/reactjs/rfcs/pull/63
  • React Issue: https://github.com/facebook/react/pull/13748

Summary

React.memo lets you memoize the render output from any component (e.g. a function component), and bail out of unnecessary updates. It is an optimization, similar to how you'd use React.PureComponent if you were writing a class.

Basic example

import { memo } from 'react';

function Button(props) {
  // Component code
}

export default memo(Button);

Wrapping a component into React.memo makes it bail out of rendering when the props are shallowly equal.

You can also pass an arePropsEqual(prevProps, nextProps) function as a second argument to customize the bailout condition:

function arePropsEqual(prevProps, nextProps) {
  return prevProps.color.id === nextProps.color.id;
}

export default memo(Button, arePropsEqual);

Motivation

Today in React, function components and PureComponent provide two different kinds of optimizations that can't be unified.

Function components let us avoid constructing a class instance. This benefits the initial render. Function components also tend to minify better than classes, which helps reduce the bundle size.

On the other hand, in order to optimize updates we sometimes want to bail out of rendering by memoizing the rendered result. To do that today, you have to convert a function component to a PureComponent class (or a class with custom shouldComponentUpdate). This is true even if you don't use features like state and other lifecycle methods. So it makes the initial render time a bit worse, but updates are potentially faster.

By having memo as a first-class API in React itself, we can remove the need to make a choice between these optimizations. It makes it easy to memoize the output of function components without introducing other extra costs.

This also helps address a common argument in teams that can't decide whether to use one or the other optimization, by letting you use them together. And unlike a userland memo() higher-order component implementation, the one built into React can be more efficient by avoiding an extra component layer.

Detailed design

React.memo returns a component type that tells React "render the inner type, but bail out on updates if props are shallowly equal". In other words, the rendering result is memoized. The prop comparison function can be specified as a second argument to React.memo.

React.memo accepts any valid component type as the first argument. This ensures that it can safely wrap an import without being concerned about its implementation details.

React.memo returns a special component type, similar to React.forwardRef. Returning an actual function or a class instead would defeat the optimization as it would create an additional layer and couldn't be faster than just React.PureComponent.

The second argument is arePropsEqual (rather than, say, arePropsDifferent) so that you can more easily pass a different off-the-shelf implementation (which tend to be written in the positive form).

Drawbacks

  • It's doable in user space (with worse performance).
  • There is some duplication with PureComponent API.
  • There may be confusion over whether the second argument is arePropsEqual or arePropsDifferent.
  • This concept was previously known as "pure component" even though it didn't match the definition of purity.

Alternatives

  • Only allow classes to have bailouts (status quo).
  • Function components could have memo by default (breaking change and potentially slow).
  • Instead of a wrapper, put a flag on the component instead.
  • Give it a long name instead of memo.
  • Call it something different like pure.
  • Don't allow to specify the custom equality function.
  • memo could be an actual higher-order component rather than return a special type.
  • memo could accept arePropsDifferent as a second argument instead.
  • memo could be constrained to only work with function components.

Adoption strategy

This is not a breaking change. You can start using function components in more places in case you previously felt limited by lack of a PureComponent alternative. You don't have to use this.

How we teach this

We're intentionally breaking away from the existing naming of PureRenderMixin and PureComponent by calling the wrapper memo. This is because conceptually this is memoization and is unrelated to the function purity.

We can now decouple teaching when to use this optimization from how to apply it, and applying it no longer forces you to rewrite a component as a class.