說到計算 render 次數,大家可能一開始想到的便是下面這段扣
const RenderCounter = () => {
const renderCountRef = useRef(0);
return <>{++renderCountRef.current}</>;
};
簡單地利用 ref 更新不會觸發 re-render 的特性,顯示出目前到底進去了幾次 render function
這個故事要說到昨天晚上,原本想說寫文章前還是用 create-react-app 測一下,上面那段扣會不會正常 work,結果在每次強制 update 時都會計算成 render 了兩次!
✨ create-react-app 貌似從 16.X 版就會自帶
React.StrictMode
了
React.StrictMode
,這是一個會在 dev 環境下會幫你 invoke 兩次 render function 的 component,讓你發現你目前的扣會不會有不正常的 side effect ?
正因為如此,所以我們的 renderCountRef.current
才會被加了兩次,你可能會想說把 React.StrictMode
拔掉不就好了,但之後換成 Concurrent Mode,就真的連他會 invoke render function 幾次都不知道了。
也就是不要在 render phase 去修改 ref 的值(global)
const RenderCounter = () => {
const renderCountRef = useRef(0);
let renderCount = renderCountRef.current;
useEffect(() => {
renderCountRef.current = renderCount;
});
renderCount += 1;
return <>{renderCount}</>;
};
利用 renderCount
這個 local variable 在每次 render function 下有 scope 的特性,也就是只要一開始的 renderCountRef.current
是正確的,他就也會是對的。
那要怎麼確保 renderCountRef.current
是正確的呢?
我們可以利用 useEffect
只會 invoke 在 commit phase 的特性,在每次 renderCount
確實有跑進去 render
function 後,再把這個值 assign 回 ref 的 current value。
可以參考 Dan Abramov 的這則 twitter
在同事 code review 後,發現可以寫得更加精簡
const RenderCounter = () => {
const renderCountRef = useRef(1);
useEffect(() => {
renderCountRef.current += 1;
});
return <>{renderCountRef.current}</>;
};