-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathindex.js
152 lines (138 loc) · 4.27 KB
/
index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
import React, { useState, useEffect, useLayoutEffect } from "react";
import ReactDOM from "react-dom";
import "./styles.css";
import renderPipeline from "./renderPipeline.jpg";
const UseEffect = () => {
const [state, setState] = useState(0);
useEffect(() => {
if (state === 0) {
setState(Math.floor(1_000 + Math.random() * 9_999));
}
}, [state]);
return (
<fieldset>
<legend>
<code>useEffect</code>
</legend>
<p>state: {state}</p>
<button onClick={() => setState(0)}>update state</button>
<p className="bad">repaint count: 2 (try to notice state flickering)</p>
<details>
<summary>explanation (click me)</summary>
<ol>
<li className="macro">macrotask / click event / setState(0)</li>
<li className="micro">microtask / react updated state</li>
<li className="micro">microtask / react patched DOM</li>
<li className="bad">browser repaints</li>
<li className="micro">microtask / useEffect / setState(random)</li>
<li className="micro">microtask / react updated state</li>
<li className="micro">microtask / react patched DOM</li>
<li className="ok">browser repaints</li>
</ol>
</details>
</fieldset>
);
};
const UseLayoutEffect = () => {
const [state, setState] = useState(0);
useLayoutEffect(() => {
if (state === 0) {
setState(Math.floor(1_000 + Math.random() * 9_999));
}
}, [state]);
return (
<fieldset>
<legend>
<code>useLayoutEffect</code>
</legend>
<p>state: {state}</p>
<button onClick={() => setState(0)}>update state</button>
<p className="ok">repaint count: 1</p>
<details>
<summary>explanation (click me)</summary>
<ol>
<li className="macro">macrotask / click event / setState(0)</li>
<li className="micro">microtask / react updated state</li>
<li className="micro">microtask / react patched DOM</li>
<li className="micro">
microtask / useLayoutEffect / setState(random)
</li>
<li className="micro">microtask / react updated state</li>
<li className="micro">microtask / react patched DOM</li>
<li className="ok">browser repaints</li>
</ol>
</details>
</fieldset>
);
};
function App() {
return (
<>
<h3>Click to compare browser's render behavior</h3>
<UseEffect />
<UseLayoutEffect />
<fieldset>
<legend>Conclusion</legend>
<p>
Try to use <code>useLayoutEffect</code> hook in cases where triggers
reflow/repaint
</p>
<table border="1" cellPadding="10px">
<tr>
<th></th>
<th>Style</th>
<th>Layout</th>
<th>Paint</th>
<th>Composite</th>
</tr>
<tr>
<th>reflow</th>
<td>✅</td>
<td>✅</td>
<td>✅</td>
<td>✅</td>
</tr>
<tr>
<th>repaint</th>
<td>✅</td>
<td></td>
<td>✅</td>
<td>✅</td>
</tr>
</table>
<img
border="1px"
src={renderPipeline}
style={{ maxWidth: "100%" }}
alt="Javascript > Style > Layout > Paint > Composite"
/>
<h3>
Use cases when to use <code>useLayoutEffect</code>:
</h3>
<ul>
<li>
if your effect changes state which triggers reflow/repaint (example
above changes number which triggers reflow)
</li>
<li>
if your effect changes <code>className</code>
</li>
<li>if you need to update DOM in your effect</li>
<li>if you need to smooth your animation</li>
<li>etc.</li>
</ul>
<p>
You can learn more about rendering process here:{" "}
<a
target="_blank"
rel="noreferrer"
href="https://developers.google.com/web/fundamentals/performance/rendering"
>
developers.google.com/web/fundamentals/performance/rendering
</a>
</p>
</fieldset>
</>
);
}
ReactDOM.render(<App />, document.querySelector("#root"));