← All React exercises

React implementation · 05

UsePrevious()

StatusNot taken

Problem

The source file does not include additional prompt text.

Solution

Review after your attempt
solution.tsx
1usePrevious() 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.