Problem
Create a hook to tell if it is the first render.
function App() {
const isFirstRender = useIsFirstRender()
// only true for the first render
...
}
React implementation · 03
Problem
Solution
Review after your attempt1We can use useRef() or useState() to store data, but here we only want to check if it is first render without triggering update, so useRef() is our choice.
2
3import { useRef } from 'react'
4function useIsFirstRender(): boolean {
5 const isFirstRender = useRef(true)
6 // TODO
7 return isFirstRender.current
8}
9
10To update the ref object, useEffect() is the first choice.
11
12import { useRef, useEffect } from 'react'
13export function useIsFirstRender(): boolean {
14 const isFirstRender = useRef(true)
15 useEffect(() => {
16 isFirstRender.current = false
17 }, [])
18 return isFirstRender.current
19}
20
21If we want a sooner timing, useLayoutEffect() could come as rescue.
22
23import { useRef, useLayoutEffect } from 'react'
24export function useIsFirstRender(): boolean {
25 const isFirstRender = useRef(true)
26 useLayoutEffect(() => {
27 isFirstRender.current = false
28 }, [])
29 return isFirstRender.current
30}
31
32Now technically "render" means the execution of the component function, if we want to strictly follow this definition, we might want to update the ref object during rendering.
33
34import { useRef, useLayoutEffect } from 'react'
35export function useIsFirstRender(): boolean {
36 const isFirstRender = useRef(true)
37 if (isFirstRender.current) {
38 isFirstRender.current = false
39 return true
40 }
41 return false
42}
43
44Notice that if "first render" is supposed to mean the first rendered UI, then above code is not guarenteed to work, following code renders "false" on the initial UI we see.
45
46export function App() {
47 const isFirstRender = useIsFirstRender()
48 const [state, setState] = useState(1)
49 if (state != 2) setState(2)
50 return <p>{String(isFirstRender)}</p>
51}
52
53Generally it is unsafe to read ref.current during rendering
54
55In above solutions, ref.current is returned in the custom hook, but generally we should avoid reading it during rendering, since it is stated clearly on React.dev:
56
57 Do not write or read ref.current during rendering, except for initialization. This makes your component’s behavior unpredictable.
58
59Here 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.
60
61Well for our simple case here, it is not a big issue, because once it is set to false, it never changes. Here is Dan's response to a similar question.