-
Notifications
You must be signed in to change notification settings - Fork 569
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
React.memo() #63
Changes from 7 commits
Commits
Show all changes
8 commits
Select commit
Hold shift + click to select a range
2b97ab0
React.pure()
gaearon 44cca38
Add more alternatives
gaearon a12900e
equal => arePropsEqual in the proposal
gaearon 93afc0d
Add naming drawback
gaearon eb6f7fc
Pure -> memo
gaearon aa27740
Update 0000-pure.md
gaearon d3c2ccb
Rename 0000-pure.md to 0000-memo.md
gaearon 530cc0d
memo() takes any component as argument
gaearon File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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 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` only accepts function components as the first argument. It doesn't accept classes since those can extend `PureComponent` directly, and mixing the two approaches is confusing. `React.memo` is intended to be applied at the definition site. | ||
|
|
||
| `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 made to work with class components too. | ||
|
|
||
| # 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. | ||
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How does this work with
React.forwardRef?Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This PR answers my question