Skip to content

React.memo() #63

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 8 commits into from
Oct 23, 2018
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
84 changes: 84 additions & 0 deletions text/0000-memo.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@

- Start Date: 2018-10-18
- RFC PR: (leave this empty)
- React Issue: (leave this empty)

# Summary

`React.memo` lets you [memoize](https://en.wikipedia.org/wiki/Memoization) 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

```js
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:

```js
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](https://en.wikipedia.org/wiki/Memoization)** 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 aobut 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.