Skip to content

Commit dec7140

Browse files
authored
Hooks FAQ: Change usePrevious recommendation (#4780)
* Hook FAQ: Change usePrevious recommendation * typo
1 parent 807fff2 commit dec7140

File tree

1 file changed

+10
-42
lines changed

1 file changed

+10
-42
lines changed

content/docs/hooks-faq.md

Lines changed: 10 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -331,54 +331,22 @@ This is a rare use case. If you need it, you can [use a mutable ref](#is-there-s
331331

332332
### How to get the previous props or state? {#how-to-get-the-previous-props-or-state}
333333

334-
Currently, you can do it manually [with a ref](#is-there-something-like-instance-variables):
334+
There are two cases in which you might want to get previous props or state.
335335

336-
```js{6,8}
337-
function Counter() {
338-
const [count, setCount] = useState(0);
339-
340-
const prevCountRef = useRef();
341-
useEffect(() => {
342-
prevCountRef.current = count;
343-
});
344-
const prevCount = prevCountRef.current;
345-
346-
return <h1>Now: {count}, before: {prevCount}</h1>;
347-
}
348-
```
349-
350-
This might be a bit convoluted but you can extract it into a custom Hook:
351-
352-
```js{3,7}
353-
function Counter() {
354-
const [count, setCount] = useState(0);
355-
const prevCount = usePrevious(count);
356-
return <h1>Now: {count}, before: {prevCount}</h1>;
357-
}
336+
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:
358337

359-
function usePrevious(value) {
360-
const ref = useRef();
361-
useEffect(() => {
362-
ref.current = value;
363-
});
364-
return ref.current;
365-
}
338+
```js
339+
useEffect(() => {
340+
ChatAPI.subscribeToSocket(props.userId);
341+
return () => ChatAPI.unsubscribeFromSocket(props.userId);
342+
}, [props.userId]);
366343
```
367344

368-
Note how this would work for props, state, or any other calculated value.
369-
370-
```js{5}
371-
function Counter() {
372-
const [count, setCount] = useState(0);
373-
374-
const calculation = count + 100;
375-
const prevCalculation = usePrevious(calculation);
376-
// ...
377-
```
345+
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.
378346

379-
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.
347+
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).
380348

381-
See also [the recommended pattern for derived state](#how-do-i-implement-getderivedstatefromprops).
349+
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.
382350

383351
### Why am I seeing stale props or state inside my function? {#why-am-i-seeing-stale-props-or-state-inside-my-function}
384352

0 commit comments

Comments
 (0)