Problem
The source file does not include additional prompt text.
React implementation · 05
Problem
Solution
Review after your attempt1usePrevious() should keep track of the previous value, we can use useRef() and update ref value in useEffect().
2
3import { useRef, useEffect} from 'react'
4export function usePrevious<T>(value: T): T | undefined {
5 const ref = useRef<T | undefined>(undefined)
6 const prev = ref.current
7 useEffect(() => {
8 ref.current = value
9 }, [value])
10 return prev
11}
12
13If we want a sooner timing, useLayoutEffect() could come as rescue.
14
15import { useRef, useLayoutEffect } from 'react'
16export function usePrevious<T>(value: T): T | undefined {
17 const ref = useRef<T | undefined>(undefined)
18 const prev = ref.current
19 useLayoutEffect(() => {
20 ref.current = value
21 }, [value])
22 return prev
23}
24
25We might want to update it even sooner right after previous value is retrieved.
26
27import { useRef, useLayoutEffect } from 'react'
28export function usePrevious<T>(value: T): T | undefined {
29 const ref = useRef<T | undefined>(undefined)
30 const prev = ref.current
31 ref.current = value
32 return prev
33}
34
35Generally it is unsafe to read or write ref.current during rendering
36
37In above solutions, ref.current is read and wrote in the custom hook, but generally we should avoid reading it during rendering, since it is stated clearly on React.dev:
38
39 Do not write or read ref.current during rendering, except for initialization. This makes your component’s behavior unpredictable.
40
41Here is how we can roughly understand why. In concurrent mode, React might render parts of our app, pause and do something more important, and then come back to render the rest of our app. This means that if we update ref.current during rendering, it could result in different parts of app using different values and thus inconsistency occurs.
42
43Well for our simple case here, it is not a big issue because useRef() is used internally in usePrevious() and only the value is returned.