Browse Source

Add more info to FAQ about deps (#1815)

* Add more info to FAQ about deps

* Update content/docs/hooks-faq.md

Co-Authored-By: gaearon <dan.abramov@gmail.com>

* Update content/docs/hooks-faq.md

Co-Authored-By: gaearon <dan.abramov@gmail.com>

* Update content/docs/hooks-faq.md

Co-Authored-By: gaearon <dan.abramov@gmail.com>

* Update content/docs/hooks-faq.md

Co-Authored-By: gaearon <dan.abramov@gmail.com>

* Update content/docs/hooks-effect.md

Co-Authored-By: gaearon <dan.abramov@gmail.com>

* tweaks

* tweak
main
Dan Abramov 6 years ago
committed by GitHub
parent
commit
2cd4d0cf5d
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 24
      content/docs/hooks-custom.md
  2. 37
      content/docs/hooks-effect.md
  3. 248
      content/docs/hooks-faq.md
  4. 41
      content/docs/hooks-reference.md
  5. 3
      content/docs/hooks-rules.md

24
content/docs/hooks-custom.md

@ -18,11 +18,11 @@ import React, { useState, useEffect } from 'react';
function FriendStatus(props) {
const [isOnline, setIsOnline] = useState(null);
function handleStatusChange(status) {
setIsOnline(status.isOnline);
}
useEffect(() => {
function handleStatusChange(status) {
setIsOnline(status.isOnline);
}
ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange);
return () => {
ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange);
@ -44,11 +44,11 @@ import React, { useState, useEffect } from 'react';
function FriendListItem(props) {
const [isOnline, setIsOnline] = useState(null);
function handleStatusChange(status) {
setIsOnline(status.isOnline);
}
useEffect(() => {
function handleStatusChange(status) {
setIsOnline(status.isOnline);
}
ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange);
return () => {
ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange);
@ -79,11 +79,11 @@ import React, { useState, useEffect } from 'react';
function useFriendStatus(friendID) {
const [isOnline, setIsOnline] = useState(null);
function handleStatusChange(status) {
setIsOnline(status.isOnline);
}
useEffect(() => {
function handleStatusChange(status) {
setIsOnline(status.isOnline);
}
ChatAPI.subscribeToFriendStatus(friendID, handleStatusChange);
return () => {
ChatAPI.unsubscribeFromFriendStatus(friendID, handleStatusChange);

37
content/docs/hooks-effect.md

@ -198,17 +198,17 @@ Let's see how we could write this component with Hooks.
You might be thinking that we'd need a separate effect to perform the cleanup. But code for adding and removing a subscription is so tightly related that `useEffect` is designed to keep it together. If your effect returns a function, React will run it when it is time to clean up:
```js{10-16}
```js{6-16}
import React, { useState, useEffect } from 'react';
function FriendStatus(props) {
const [isOnline, setIsOnline] = useState(null);
function handleStatusChange(status) {
setIsOnline(status.isOnline);
}
useEffect(() => {
function handleStatusChange(status) {
setIsOnline(status.isOnline);
}
ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange);
// Specify how to clean up after this effect:
return function cleanup() {
@ -237,6 +237,10 @@ We've learned that `useEffect` lets us express different kinds of side effects a
```js
useEffect(() => {
function handleStatusChange(status) {
setIsOnline(status.isOnline);
}
ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange);
return () => {
ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange);
@ -316,15 +320,15 @@ function FriendStatusWithCounter(props) {
const [isOnline, setIsOnline] = useState(null);
useEffect(() => {
function handleStatusChange(status) {
setIsOnline(status.isOnline);
}
ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange);
return () => {
ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange);
};
});
function handleStatusChange(status) {
setIsOnline(status.isOnline);
}
// ...
}
```
@ -394,6 +398,7 @@ Now consider the version of this component that uses Hooks:
function FriendStatus(props) {
// ...
useEffect(() => {
// ...
ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange);
return () => {
ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange);
@ -449,8 +454,12 @@ When we render with `count` updated to `6`, React will compare the items in the
This also works for effects that have a cleanup phase:
```js{6}
```js{10}
useEffect(() => {
function handleStatusChange(status) {
setIsOnline(status.isOnline);
}
ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange);
return () => {
ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange);
@ -462,9 +471,13 @@ In the future, the second argument might get added automatically by a build-time
>Note
>
>If you use this optimization, make sure the array includes **any values from the outer scope that change over time and that are used by the effect**. Otherwise, your code will reference stale values from previous renders. We'll also discuss other optimization options in the [Hooks API reference](/docs/hooks-reference.html).
>If you use this optimization, make sure the array includes **all values from the component scope (such as props and state) that change over time and that are used by the effect**. Otherwise, your code will reference stale values from previous renders. Learn more about [how to deal with functions](/docs/hooks-faq.html#is-it-safe-to-omit-functions-from-the-list-of-dependencies) and [what to do when the array changes too often](/docs/hooks-faq.html#what-can-i-do-if-my-effect-dependencies-change-too-often).
>
>If you want to run an effect and clean it up only once (on mount and unmount), you can pass an empty array (`[]`) as a second argument. This tells React that your effect doesn't depend on *any* values from props or state, so it never needs to re-run. This isn't handled as a special case -- it follows directly from how the inputs array always works.
>
>If you pass an empty array (`[]`), the props and state inside the effect will always have their initial values. While passing `[]` as the second argument is closer to the familiar `componentDidMount` and `componentWillUnmount` mental model, there are usually [better](/docs/hooks-faq.html#is-it-safe-to-omit-functions-from-the-list-of-dependencies) [solutions](/docs/hooks-faq.html#what-can-i-do-if-my-effect-dependencies-change-too-often) to avoid re-running effects too often. Also, don't forget that React defers running `useEffect` until after the browser has painted, so doing extra work is less of a problem.
>
>If you want to run an effect and clean it up only once (on mount and unmount), you can pass an empty array (`[]`) as a second argument. This tells React that your effect doesn't depend on *any* values from props or state, so it never needs to re-run. This isn't handled as a special case -- it follows directly from how the inputs array always works. While passing `[]` is closer to the familiar `componentDidMount` and `componentWillUnmount` mental model, we suggest not making it a habit because it often leads to bugs, [as discussed above](#explanation-why-effects-run-on-each-update). Don't forget that React defers running `useEffect` until after the browser has painted, so doing extra work is less of a problem.
>We recommend using the [`exhaustive-deps`](https://github.com/facebook/react/issues/14920) rule as part of our [`eslint-plugin-react-hooks`](https://www.npmjs.com/package/eslint-plugin-react-hooks#installation) package. It warns when dependencies are specified incorrectly and suggests a fix.
## Next Steps {#next-steps}

248
content/docs/hooks-faq.md

@ -32,16 +32,20 @@ This page answers some of the frequently asked questions about [Hooks](/docs/hoo
* [What exactly do the lint rules enforce?](#what-exactly-do-the-lint-rules-enforce)
* **[From Classes to Hooks](#from-classes-to-hooks)**
* [How do lifecycle methods correspond to Hooks?](#how-do-lifecycle-methods-correspond-to-hooks)
* [How can I do data fetching with Hooks?](#how-can-i-do-data-fetching-with-hooks)
* [Is there something like instance variables?](#is-there-something-like-instance-variables)
* [Should I use one or many state variables?](#should-i-use-one-or-many-state-variables)
* [Can I run an effect only on updates?](#can-i-run-an-effect-only-on-updates)
* [How to get the previous props or state?](#how-to-get-the-previous-props-or-state)
* [Why am I seeing stale props or state inside my function?](#why-am-i-seeing-stale-props-or-state-inside-my-function)
* [How do I implement getDerivedStateFromProps?](#how-do-i-implement-getderivedstatefromprops)
* [Is there something like forceUpdate?](#is-there-something-like-forceupdate)
* [Can I make a ref to a function component?](#can-i-make-a-ref-to-a-function-component)
* [What does const [thing, setThing] = useState() mean?](#what-does-const-thing-setthing--usestate-mean)
* **[Performance Optimizations](#performance-optimizations)**
* [Can I skip an effect on updates?](#can-i-skip-an-effect-on-updates)
* [Is it safe to omit functions from the list of dependencies?](#is-it-safe-to-omit-functions-from-the-list-of-dependencies)
* [What can I do if my effect dependencies change too often?](#what-can-i-do-if-my-effect-dependencies-change-too-often)
* [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)
@ -204,6 +208,10 @@ There are a few more heuristics, and they might change over time as we fine-tune
* `componentDidCatch` and `getDerivedStateFromError`: There are no Hook equivalents for these methods yet, but they will be added soon.
### How can I do data fetching with Hooks?
Check out [this article](https://www.robinwieruch.de/react-hooks-fetch-data/) to learn more about data fetching with Hooks.
### Is there something like instance variables? {#is-there-something-like-instance-variables}
Yes! The [`useRef()`](/docs/hooks-reference.html#useref) Hook isn't just for DOM refs. The "ref" object is a generic container whose `current` property is mutable and can hold any value, similar to an instance property on a class.
@ -362,6 +370,44 @@ It's possible that in the future React will provide a `usePrevious` Hook out of
See also [the recommended pattern for derived state](#how-do-i-implement-getderivedstatefromprops).
### Why am I seeing stale props or state inside my function? {#why-am-i-seeing-stale-props-or-state-inside-my-function}
Any function inside a component, including event handlers and effects, "sees" the props and state from the render it was created in. For example, consider code like this:
```js
function Example() {
const [count, setCount] = useState(0);
function handleAlertClick() {
setTimeout(() => {
alert('You clicked on: ' + count);
}, 3000);
}
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>
Click me
</button>
<button onClick={handleAlertClick}>
Show alert
</button>
</div>
);
}
```
If you first click "Show alert" and then increment the counter, the alert will show the `count` variable **at the time you clicked the "Show alert" button**. This prevents bugs caused by the code assuming props and state don't change.
If you intentionally want to read the *latest* state from some asynchronous callback, you could keep it in [a ref](/docs/hooks-faq.html#is-there-something-like-instance-variables), mutate it, and read from it.
Finally, another possible reason you're seeing stale props or state is if you use the "dependency array" optimization but didn't correctly specify all the dependencies. For example, if an effect specifies `[]` as the second argument but reads `someProp` inside, it will keep "seeing" the initial value of `someProp`. The solution is to either remove the dependency array, or to fix it. Here's [how you can deal with functions](#is-it-safe-to-omit-functions-from-the-list-of-dependencies), and here's [other common strategies](#what-can-i-do-if-my-effect-dependencies-change-too-often) to run effects less often without incorrectly skipping dependencies.
>Note
>
>We provide an [`exhaustive-deps`](https://github.com/facebook/react/issues/14920) ESLint rule as a part of the [`eslint-plugin-react-hooks`](https://www.npmjs.com/package/eslint-plugin-react-hooks#installation) package. It warns when dependencies are specified incorrectly and suggests a fix.
### How do I implement `getDerivedStateFromProps`? {#how-do-i-implement-getderivedstatefromprops}
While you probably [don't need it](/blog/2018/06/07/you-probably-dont-need-derived-state.html), in rare cases that you do (such as implementing a `<Transition>` component), you can update the state right during rendering. React will re-run the component with updated state immediately after exiting the first render so it wouldn't be expensive.
@ -416,6 +462,207 @@ If you're not familiar with this syntax, check out the [explanation](/docs/hooks
Yes. See [conditionally firing an effect](/docs/hooks-reference.html#conditionally-firing-an-effect). Note that forgetting to handle updates often [introduces bugs](/docs/hooks-effect.html#explanation-why-effects-run-on-each-update), which is why this isn't the default behavior.
### Is it safe to omit functions from the list of dependencies? {#is-it-safe-to-omit-functions-from-the-list-of-dependencies}
Generally speaking, no.
```js{3,8}
function Example() {
function doSomething() {
console.log(someProp);
}
useEffect(() => {
doSomething();
}, []); // 🔴 This is not safe (it calls `doSomething` which uses `someProp`)
}
```
It's difficult to remember which props or state are used by functions outside of the effect. This is why **usually you'll want to declare functions needed by an effect *inside* of it.** Then it's easy to see what values from the component scope that effect depends on:
```js{4,8}
function Example() {
useEffect(() => {
function doSomething() {
console.log(someProp);
}
doSomething();
}, [someProp]); // ✅ OK (our effect only uses `someProp`)
}
```
If after that we still don't use any values from the component scope, it's safe to specify `[]`:
```js{7}
useEffect(() => {
function doSomething() {
console.log('hello');
}
doSomething();
}, []); // ✅ OK in this example because we don't use *any* values from component scope
```
Depending on your use case, there are a few more options described below.
>Note
>
>We provide the [`exhaustive-deps`](https://github.com/facebook/react/issues/14920) ESLint rule as a part of the [`eslint-plugin-react-hooks`](https://www.npmjs.com/package/eslint-plugin-react-hooks#installation) package. It help you find components that don't handle updates consistently.
Let's see why this matters.
If you specify a [list of dependencies](/docs/hooks-reference.html#conditionally-firing-an-effect) as the last argument to `useEffect`, `useMemo`, `useCallback`, or `useImperativeHandle`, it must include all values used inside that participate in the React data flow. That includes props, state, and anything derived from them.
It is **only** safe to omit a function from the dependency list if nothing in it (or the functions called by it) references props, state, or values derived from them. This example has a bug:
```js{5,12}
function ProductPage({ productId }) {
const [product, setProduct] = useState(null);
async function fetchProduct() {
const response = await fetch('http://myapi/product' + productId); // Uses productId prop
const json = await response.json();
setProduct(json);
}
useEffect(() => {
fetchProduct();
}, []); // 🔴 Invalid because `fetchProduct` uses `productId`
// ...
}
```
**The recommended fix is to move that function _inside_ of your effect**. That makes it easy to see which props or state your effect uses, and to ensure they're all declared:
```js{5-10,13}
function ProductPage({ productId }) {
const [product, setProduct] = useState(null);
useEffect(() => {
// By moving this function inside the effect, we can clearly see the values it uses.
async function fetchProduct() {
const response = await fetch('http://myapi/product' + productId);
const json = await response.json();
setProduct(json);
}
fetchProduct();
}, [productId]); // ✅ Valid because our effect only uses productId
// ...
}
```
This also allows you to handle out-of-order responses with a local variable inside the effect:
```js{2,6,8}
useEffect(() => {
let ignore = false;
async function fetchProduct() {
const response = await fetch('http://myapi/product/' + productId);
const json = await response.json();
if (!ignore) setProduct(json);
}
return () => { ignore = true };
}, [productId]);
```
We moved the function inside the effect so it doesn't need to be in its dependency list.
>Tip
>
>Check out [this article](https://www.robinwieruch.de/react-hooks-fetch-data/) to learn more about data fetching with Hooks.
**If for some reason you _can't_ move a function inside an effect, there are a few more options:**
* **You can try moving that function outside of your component**. In that case, the function is guaranteed to not reference any props or state, and also doesn't need to be in the list of dependencies.
* If the function you're calling is a pure computation and is safe to call while rendering, you may **call it outside of the effect instead,** and make the effect depend on the returned value.
* As a last resort, you can **add a function to effect dependencies but _wrap its definition_** into the [`useCallback`](/docs/hooks-reference.html#usecallback) Hook. This ensures it doesn't change on every render unless *its own* dependencies also change:
```js{2-5}
function ProductPage({ productId }) {
// ✅ Wrap with useCallback to avoid change on every render
const fetchProduct = useCallback(() => {
// ... Does something with productId ...
}, [productId]); // ✅ All useCallback dependencies are specified
return <ProductDetails fetchProduct={fetchProduct} />;
}
function ProductDetails({ fetchProduct })
useEffect(() => {
fetchProduct();
}, [fetchProduct]); // ✅ All useEffect dependencies are specified
// ...
}
```
Note that in the above example we **need** to keep the function in the dependencies list. This ensures that a change in the `productId` prop of `ProductPage` automatically triggers a refetch in the `ProductDetails` component.
### What can I do if my effect dependencies change too often?
Sometimes, your effect may be using reading state that changes too often. You might be tempted to omit that state from a list of dependencies, but that usually leads to bugs:
```js{6,9}
function Counter() {
const [count, setCount] = useState(0);
useEffect(() => {
const id = setInterval(() => {
setCount(count + 1); // This effect depends on the `count` state
}, 1000);
return () => clearInterval(id);
}, []); // 🔴 Bug: `count` is not specified as a dependency
return <h1>{count}</h1>;
}
```
Specifying `[count]` as a list of dependencies would fix the bug, but would cause the interval to be reset on every change. That may not be desirable. To fix this, we can use the [functional update form of `setState`](/docs/hooks-reference.html#functional-updates). It lets us specify *how* the state needs to change without referencing the *current* state:
```js{6,9}
function Counter() {
const [count, setCount] = useState(0);
useEffect(() => {
const id = setInterval(() => {
setCount(c => c + 1); // ✅ This doesn't depend on `count` variable outside
}, 1000);
return () => clearInterval(id);
}, []); // ✅ Our effect doesn't use any variables in the component scope
return <h1>{count}</h1>;
}
```
(The identity of the `setCount` function is guaranteed to be stable so it's safe to omit.)
In more complex cases (such as if one state depends on another state), try moving the state update logic outside the effect with the [`useReducer` Hook](/docs/hooks-reference.html#usereducer). [This article](https://adamrackis.dev/state-and-use-reducer/) offers an example of how you can do this. **The identity of the `dispatch` function from `useReducer` is always stable** — even if the reducer function is declared inside the component and reads its props.
As a last resort, if you want to something like `this` in a class, you can [use a ref](/docs/hooks-faq.html#is-there-something-like-instance-variables) to hold a mutable variable. Then you can write and read to it. For example:
```js{2-6,10-11,16}
function Example(props) {
// Keep latest props in a ref.
let latestProps = useRef(props);
useEffect(() => {
latestProps.current = props;
});
useEffect(() => {
function tick() {
// Read latest props at any time
console.log(latestProps.current);
}
const id = setInterval(tick, 1000);
return () => clearInterval(id);
}, []); // This effect never re-runs
}
```
Only do this if you couldn't find a better alternative, as relying on mutation makes components less predictable. If there's a specific pattern that doesn't translate well, [file an issue](https://github.com/facebook/react/issues/new) with a runnable example code and we can try to help.
### How do I implement `shouldComponentUpdate`? {#how-do-i-implement-shouldcomponentupdate}
You can wrap a function component with `React.memo` to shallowly compare its props:
@ -430,7 +677,6 @@ It's not a Hook because it doesn't compose like Hooks do. `React.memo` is equiva
`React.memo` doesn't compare state because there is no single state object to compare. But you can make children pure too, or even [optimize individual children with `useMemo`](/docs/hooks-faq.html#how-to-memoize-calculations).
### How to memoize calculations? {#how-to-memoize-calculations}
The [`useMemo`](/docs/hooks-reference.html#usememo) Hook lets you cache calculations between multiple renders by "remembering" the previous computation:

41
content/docs/hooks-reference.md

@ -45,6 +45,10 @@ setState(newState);
During subsequent re-renders, the first value returned by `useState` will always be the most recent state after applying updates.
>Note
>
>React guarantees that `setState` function identity is stable and won't change on re-renders. This is why it's safe to omit from the `useEffect` or `useCallback` dependency list.
#### Functional updates {#functional-updates}
If the new state is computed using the previous state, you can pass a function to `setState`. The function will receive the previous value, and return an updated value. Here's an example of a counter component that uses both forms of `setState`:
@ -133,7 +137,7 @@ Although `useEffect` is deferred until after the browser has painted, it's guara
#### Conditionally firing an effect {#conditionally-firing-an-effect}
The default behavior for effects is to fire the effect after every completed render. That way an effect is always recreated if one of its inputs changes.
The default behavior for effects is to fire the effect after every completed render. That way an effect is always recreated if one of its dependencies changes.
However, this may be overkill in some cases, like the subscription example from the previous section. We don't need to create a new subscription on every update, only if the `source` props has changed.
@ -153,11 +157,18 @@ useEffect(
Now the subscription will only be recreated when `props.source` changes.
Passing in an empty array `[]` of inputs tells React that your effect doesn't depend on any values from the component, so that effect would run only on mount and clean up on unmount; it won't run on updates.
> Note
>Note
>
>If you use this optimization, make sure the array includes **all values from the component scope (such as props and state) that change over time and that are used by the effect**. Otherwise, your code will reference stale values from previous renders. Learn more about [how to deal with functions](/docs/hooks-faq.html#is-it-safe-to-omit-functions-from-the-list-of-dependencies) and what to do when the [array values change too often](/docs/hooks-faq.html#what-can-i-do-if-my-effect-dependencies-change-too-often).
>
>If you want to run an effect and clean it up only once (on mount and unmount), you can pass an empty array (`[]`) as a second argument. This tells React that your effect doesn't depend on *any* values from props or state, so it never needs to re-run. This isn't handled as a special case -- it follows directly from how the dependencies array always works.
>
> The array of inputs is not passed as arguments to the effect function. Conceptually, though, that's what they represent: every value referenced inside the effect function should also appear in the inputs array. In the future, a sufficiently advanced compiler could create this array automatically.
>If you pass an empty array (`[]`), the props and state as inside the effect will always have their initial values. While passing `[]` as the second argument is closer to the familiar `componentDidMount` and `componentWillUnmount` mental model, there are usually [better](/docs/hooks-faq.html#is-it-safe-to-omit-functions-from-the-list-of-dependencies) [solutions](/docs/hooks-faq.html#what-can-i-do-if-my-effect-dependencies-change-too-often) to avoid re-running effects too often. Also, don't forget that React defers running `useEffect` until after the browser has painted, so doing extra work is less of a problem.
>
>
>We recommend using the [`exhaustive-deps`](https://github.com/facebook/react/issues/14920) rule as part of our [`eslint-plugin-react-hooks`](https://www.npmjs.com/package/eslint-plugin-react-hooks#installation) package. It warns when dependencies are specified incorrectly and suggests a fix.
The array of dependencies is not passed as arguments to the effect function. Conceptually, though, that's what they represent: every value referenced inside the effect function should also appear in the dependencies array. In the future, a sufficiently advanced compiler could create this array automatically.
### `useContext` {#usecontext}
@ -211,6 +222,10 @@ function Counter({initialState}) {
}
```
>Note
>
>React guarantees that `dispatch` function identity is stable and won't change on re-renders. This is why it's safe to omit from the `useEffect` or `useCallback` dependency list.
#### Specifying the initial state {#specifying-the-initial-state}
There’s two different ways to initialize `useReducer` state. You may choose either one depending on the use case. The simplest way to pass the initial state as a second argument:
@ -283,13 +298,15 @@ const memoizedCallback = useCallback(
Returns a [memoized](https://en.wikipedia.org/wiki/Memoization) callback.
Pass an inline callback and an array of inputs. `useCallback` will return a memoized version of the callback that only changes if one of the inputs has changed. This is useful when passing callbacks to optimized child components that rely on reference equality to prevent unnecessary renders (e.g. `shouldComponentUpdate`).
Pass an inline callback and an array of dependencies. `useCallback` will return a memoized version of the callback that only changes if one of the dependencies has changed. This is useful when passing callbacks to optimized child components that rely on reference equality to prevent unnecessary renders (e.g. `shouldComponentUpdate`).
`useCallback(fn, inputs)` is equivalent to `useMemo(() => fn, inputs)`.
`useCallback(fn, deps)` is equivalent to `useMemo(() => fn, deps)`.
> Note
>
> The array of inputs is not passed as arguments to the callback. Conceptually, though, that's what they represent: every value referenced inside the callback should also appear in the inputs array. In the future, a sufficiently advanced compiler could create this array automatically.
> The array of dependencies is not passed as arguments to the callback. Conceptually, though, that's what they represent: every value referenced inside the callback should also appear in the dependencies array. In the future, a sufficiently advanced compiler could create this array automatically.
>
> We recommend using the [`exhaustive-deps`](https://github.com/facebook/react/issues/14920) rule as part of our [`eslint-plugin-react-hooks`](https://www.npmjs.com/package/eslint-plugin-react-hooks#installation) package. It warns when dependencies are specified incorrectly and suggests a fix.
### `useMemo` {#usememo}
@ -299,7 +316,7 @@ const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);
Returns a [memoized](https://en.wikipedia.org/wiki/Memoization) value.
Pass a "create" function and an array of inputs. `useMemo` will only recompute the memoized value when one of the inputs has changed. This optimization helps to avoid expensive calculations on every render.
Pass a "create" function and an array of dependencies. `useMemo` will only recompute the memoized value when one of the dependencies has changed. This optimization helps to avoid expensive calculations on every render.
Remember that the function passed to `useMemo` runs during rendering. Don't do anything there that you wouldn't normally do while rendering. For example, side effects belong in `useEffect`, not `useMemo`.
@ -309,7 +326,9 @@ If no array is provided, a new value will be computed whenever a new function in
> 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.
> The array of dependencies 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 dependencies array. In the future, a sufficiently advanced compiler could create this array automatically.
>
> We recommend using the [`exhaustive-deps`](https://github.com/facebook/react/issues/14920) rule as part of our [`eslint-plugin-react-hooks`](https://www.npmjs.com/package/eslint-plugin-react-hooks#installation) package. It warns when dependencies are specified incorrectly and suggests a fix.
### `useRef` {#useref}
@ -342,7 +361,7 @@ Note that `useRef()` is useful for more than the `ref` attribute. It's [handy fo
### `useImperativeHandle` {#useimperativehandle}
```js
useImperativeHandle(ref, createHandle, [inputs])
useImperativeHandle(ref, createHandle, [deps])
```
`useImperativeHandle` customizes the instance value that is exposed to parent components when using `ref`. As always, imperative code using refs should be avoided in most cases. `useImperativeHandle` should be used with `forwardRef`:

3
content/docs/hooks-rules.md

@ -40,7 +40,8 @@ npm install eslint-plugin-react-hooks
],
"rules": {
// ...
"react-hooks/rules-of-hooks": "error"
"react-hooks/rules-of-hooks": "error", // Checks rules of Hooks
"react-hooks/exhaustive-deps": "warning" // Checks effect dependencies
}
}
```

Loading…
Cancel
Save