Browse Source

Hooks FAQ: Change usePrevious recommendation (#4780)

* Hook FAQ: Change usePrevious recommendation

* typo
main
dan 3 years ago
committed by GitHub
parent
commit
dec71401e3
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 52
      content/docs/hooks-faq.md

52
content/docs/hooks-faq.md

@ -331,54 +331,22 @@ This is a rare use case. If you need it, you can [use a mutable ref](#is-there-s
### How to get the previous props or state? {#how-to-get-the-previous-props-or-state}
Currently, you can do it manually [with a ref](#is-there-something-like-instance-variables):
There are two cases in which you might want to get previous props or state.
```js{6,8}
function Counter() {
const [count, setCount] = useState(0);
const prevCountRef = useRef();
useEffect(() => {
prevCountRef.current = count;
});
const prevCount = prevCountRef.current;
return <h1>Now: {count}, before: {prevCount}</h1>;
}
```
This might be a bit convoluted but you can extract it into a custom Hook:
```js{3,7}
function Counter() {
const [count, setCount] = useState(0);
const prevCount = usePrevious(count);
return <h1>Now: {count}, before: {prevCount}</h1>;
}
Sometimes, you need previous props to **clean up an effect.** For example, you might have an effect that subscribes to a socket based on the `userId` prop. If the `userId` prop changes, you want to unsubscribe from the _previous_ `userId` and subscribe to the _next_ one. You don't need to do anything special for this to work:
function usePrevious(value) {
const ref = useRef();
useEffect(() => {
ref.current = value;
});
return ref.current;
}
```js
useEffect(() => {
ChatAPI.subscribeToSocket(props.userId);
return () => ChatAPI.unsubscribeFromSocket(props.userId);
}, [props.userId]);
```
Note how this would work for props, state, or any other calculated value.
```js{5}
function Counter() {
const [count, setCount] = useState(0);
const calculation = count + 100;
const prevCalculation = usePrevious(calculation);
// ...
```
In the above example, if `userId` changes from `3` to `4`, `ChatAPI.unsubscribeFromSocket(3)` will run first, and then `ChatAPI.subscribeToSocket(4)` will run. There is no need to get "previous" `userId` because the cleanup function will capture it in a closure.
It's possible that in the future React will provide a `usePrevious` Hook out of the box since it's a relatively common use case.
Other times, you might need to **adjust state based on a change in props or other state**. This is rarely needed and is usually a sign you have some duplicate or redundant state. However, in the rare case that you need this pattern, you can [store previous state or props in state and update them during rendering](#how-do-i-implement-getderivedstatefromprops).
See also [the recommended pattern for derived state](#how-do-i-implement-getderivedstatefromprops).
We have previously suggested a custom Hook called `usePrevious` to hold the previous value. However, we've found that most use cases fall into the two patterns described above. If your use case is different, you can [hold a value in a ref](#is-there-something-like-instance-variables) and manually update it when needed. Avoid reading and updating refs during rendering because this makes your component's behavior difficult to predict and understand.
### Why am I seeing stale props or state inside my function? {#why-am-i-seeing-stale-props-or-state-inside-my-function}

Loading…
Cancel
Save