為了生成唯一的Id,React 18 後提供了一個新的 Hook => useId
const id = useId();
他的基本原理表示著,每個 id 都代表該元件在元件樹中的層級結構。
注意:
useId
不建議用於當做 HTML 元素列表的 Key,列表的 Key 應該要於你傳入要渲染出畫面的資料有關。
一個簡單的作法,可以使用 useId 產生一個 id,代表某個 DOM 的 Id
function Checkbox() {
const id = useId();
return (
<>
<label htmlFor={id}>Do you like React?</label>
<input id={id} type="checkbox" name="react"/>
</>
);
};
如果同一元件內的多個 DOM 元素需要時,那麼我們可以先用 useId 產生完代表這個元件的 id,然後再為其加上後綴
function NameFields() {
const id = useId();
return (
<div>
<label htmlFor={id + '-firstName'}>First Name</label>
<div>
<input id={id + '-firstName'} type="text" />
</div>
<label htmlFor={id + '-lastName'}>Last Name</label>
<div>
<input id={id + '-lastName'} type="text" />
</div>
</div>
);
}
注意:
useId
的生成結果字串內會包含:
這個符號,用以確保此字串是唯一的,但這樣的字串結果就無法應用在 CSS Selector 或 querySelectorAll 等 API 中,必需要另外定義適合的 Selector。
它和 useEffect
的語法、使用上一模一樣。
會有以下幾點差異:
useLayoutEffect
在 Virtual-DOM 更新完,但在 Render 之前執行,useEffect
則是在 Render 之後執行。useLayoutEffect
是同步執行的,UI 會等 useLayoutEffect
要處理的任務做完才會做 Render。所以不要在 useLayoutEffect
中做需要大量計算的處理,會導致頁面卡頓。useEffect
是異步執行的,UI Render 和 useEffect
不會有有要排隊依序執行的概念。當你希望畫面不要有閃一下的情況時,可以使用
useLayoutEffect
,因為他會在 Render 之前執新。但如果在
useLayoutEffect
中有大量計算的處理,因為 UI 會等它做完才 Render,這時畫面反而會空白一陣子,造成 UX 體驗不好。有時我們可以透過加上過場效果之類的 UX 解決方式,還是能使用
useEffect
完成處理。
function EffectComponent() {
const [count, setCount] = React.useState(0);
React.useEffect(() => {
console.log(`useEffect - count=${count}`)
// 耗時的操作
const pre = Date.now();
while(Date.now() - pre < 500) {}
// count為0時重新生成個隨機數
if (count === 0) {
setCount(10 + Math.random() * 200);
}
}, [count]);
return (
<div className= 'EffectComponent'
onClick={() => setCount(0)}>
<h2>EffectComponent</h2>
<div>{count}</div>
</div>
);
}
useEffect 因為是 Render 之後才執行,所以會看到展示 0 的過程
function LayoutEffectComponent() {
const [count, setCount] = React.useState(0);
React.useLayoutEffect(() => {
console.log(`useLayoutEffect - count=${count}`)
// 耗時的操作
const pre = Date.now();
while(Date.now() - pre < 500) {}
// count為0時重新生成個隨機數
if (count === 0) {
setCount(10 + Math.random() * 200);
}
}, [count]);
return (
<div className= 'LayoutEffectComponent'
onClick={() => setCount(0)}>
<h2>LayoutEffectComponent</h2>
<div>{count}</div>
</div>
);
}
- 沒有閃爍,當點擊 div,count 更新為0,此時頁面並不會渲染,而是等待
useLayoutEffect
內部狀態修改後,才會去更新頁面,所以頁面不會閃爍。- 但是也可以發現頁面更新的比較卡頓,因為
useLayoutEffect
會阻塞瀏覽器渲染,正好本例中useLayoutEffect
的裡有個耗時操作,所以頁面更新比較卡頓。
完整執行結果:https://codepen.io/lala-lee-jobs/pen/BaxpaqG?editors=0111
接下來介紹跟效能優化有關的二個 Hook - useMemo
及 useCallback
,以及一個 Higher-Order Component - React.memo
,來幫助我們解決 React Render 時效能上的問題。
https://zh-hans.reactjs.org/docs/hooks-reference.html#useid
https://zh-hant.reactjs.org/docs/hooks-reference.html#uselayouteffect
https://segmentfault.com/a/1190000023396433