Browse Source

[Hooks] Add recommendations about useMemo and lazy init (#1565)

main
Dan Abramov 6 years ago
committed by GitHub
parent
commit
d7d5533278
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 67
      content/docs/hooks-faq.md
  2. 2
      content/docs/hooks-reference.md

67
content/docs/hooks-faq.md

@ -41,6 +41,7 @@ This page answers some of the frequently asked questions about [Hooks](/docs/hoo
* [Can I skip an effect on updates?](#can-i-skip-an-effect-on-updates)
* [How do I implement shouldComponentUpdate?](#how-do-i-implement-shouldcomponentupdate)
* [How to memoize calculations?](#how-to-memoize-calculations)
* [How to create expensive objects lazily?](#how-to-create-expensive-objects-lazily)
* [Are Hooks slow because of creating functions in render?](#are-hooks-slow-because-of-creating-functions-in-render)
* [How to avoid passing callbacks down?](#how-to-avoid-passing-callbacks-down)
* [How to read an often-changing value from useCallback?](#how-to-read-an-often-changing-value-from-usecallback)
@ -342,7 +343,9 @@ const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);
This code calls `computeExpensiveValue(a, b)`. But if the inputs `[a, b]` haven't changed since the last value, `useMemo` skips calling it a second time and simply reuses the last value it returned.
Conveniently, this also lets you skip an expensive re-render of a child:
`useMemo` is treated as a hint rather than guarantee. React may still choose to "forget" some previously memoized values to free memory, and recalculate them on next render.
Conveniently, `useMemo` also lets you skip an expensive re-render of a child:
```js
function Parent({ a, b }) {
@ -361,6 +364,67 @@ function Parent({ a, b }) {
Note that this approach won't work in a loop because Hook calls [can't](/docs/hooks-rules.html) be placed inside loops. But you can extract a separate component for the list item, and call `useMemo` there.
### How to create expensive objects lazily?
`useMemo` lets you [memoize an expensive calculation](#how-to-memoize-calculations) if the inputs are the same. However, it only serves as a hint, and doesn't *guarantee* the computation won't re-run. But sometimes need to be sure an object is only created once.
**The first common use case is when creating the initial state is expensive:**
```js
function Table(props) {
// ⚠️ createRows() is called on every render
const [rows, setRows] = useState(createRows(props.count));
// ...
}
```
To avoid re-creating the ignored initial state, we can pass a **function** to `useState`:
```js
function Table(props) {
// ✅ createRows() is only called once
const [rows, setRows] = useState(() => createRows(props.count));
// ...
}
```
React will only call this function during the first render. See the [`useState` API reference](/docs/hooks-reference.html#usestate).
**You might also occasionally want to avoid re-creating the `useRef()` initial value.** For example, maybe you want to ensure some imperative class instance only gets created once:
```js
function Image(props) {
// ⚠️ IntersectionObserver is created on every render
const ref = useRef(new IntersectionObserver(onIntersect));
// ...
}
```
`useRef` **does not** accept a special function overload like `useState`. Instead, you can write your own function that creates and sets it lazily:
```js
function Image(props) {
const ref = useRef(null);
// ✅ IntersectionObserver is created lazily once
function getObserver() {
let observer = ref.current;
if (observer !== null) {
return observer;
}
let newObserver = new IntersectionObserver(onIntersect);
ref.current = newObserver;
return newObserver;
}
// When you need it, call getObserver()
// ...
}
```
This avoids creating an expensive object until it's truly needed for the first time. If you use Flow or TypeScript, you can also give `getObserver()` a non-nullable type for convenience.
### Are Hooks slow because of creating functions in render?
No. In modern browsers, the raw performance of closures compared to classes doesn't differ significantly except in extreme scenarios.
@ -497,6 +561,7 @@ function useEventCallback(fn, dependencies) {
In either case, we **don't recommend this pattern** and only show it here for completeness. Instead, it is preferable to [avoid passing callbacks deep down](#how-to-avoid-passing-callbacks-down).
## Under the Hood
### How does React associate Hook calls with components?

2
content/docs/hooks-reference.md

@ -290,6 +290,8 @@ Pass a "create" function and an array of inputs. `useMemo` will only recompute t
If no array is provided, a new value will be computed whenever a new function instance is passed as the first argument. (With an inline function, on every render.)
**Don't rely on `useMemo` for correctness.** React treats it as an optimization hint and does not *guarantee* to retain the memoized value. For example, React may choose to "forget" some previously memoized values to free memory, and recalculate them on next render.
> Note
>
> The array of inputs is not passed as arguments to the function. Conceptually, though, that's what they represent: every value referenced inside the function should also appear in the inputs array. In the future, a sufficiently advanced compiler could create this array automatically.

Loading…
Cancel
Save