iT邦幫忙

第 12 屆 iThome 鐵人賽

DAY 8
0
Modern Web

ReactJS 疑難排解系列 第 8

ReactJS 疑難排解:畫面更新時會閃爍,試著用 useLayoutEffect 吧

在開始之前,先來看看以下的例子

function Example() {
  const [height, setHeight] = useState(50);
  useEffect(() => {
    if (height === 50) {
      setHeight(100);
    }
  }, [height]);

  return (
    <>
      <div style={{ width: 100, height, background: "hotpink" }}/>
      <button onClick={() => setHeight(50)}>change height!</button>
    </>
  );
}

點擊 button 後,你會發現

咦! 上面那塊正方形怎麼會閃爍 /images/emoticon/emoticon06.gif

找出原因

在這個例子中我們使用 useEffect 讓 height 更新後再去更新一次,觸發 re-render
理論上兩次 setState 應該會 batch 在一起,只 render 一次呀

但這麼想錯的!


我們可以先看一下 Docs 上怎麼說

Effect 的時機
與 componentDidMount 和 componentDidUpdate 不同,在延遲事件期間,傳遞給 useEffect 的 function 會在 layout 和 render 之後觸發。

沒錯,如同上面所述,useEffect 的觸發時機會被延遲到 DOM 繪制完成。因此我們點擊按鈕 setState 後,其實已經經過 render -> commit DOM -> DOM render 的過程,所以才會被看到中途 render 完後的過程。正因為如此,肉眼才會看到中間過渡的 state 導致有閃爍的感覺。

閃爍的救星:useLayoutEffect

節錄自 Docs

useLayoutEffect 會在所有 DOM 改變後,同步調用。使用它來讀取 DOM layout 並同步重新 render。
在瀏覽器執行繪製之前,useLayoutEffect 內部的更新將被同步刷新。

正因為這個 hook 的特性,我們可以使用它來讓 DOM 的渲染慢一拍,等待 state 真正更新完後才去渲染瀏覽器的畫面。

要注意的是,使用 useLayoutEffect 會導致渲染被阻塞,所以不要拿來跑太複雜的計算
正如同文件上所說,決大部分的場景,都不應該阻礙瀏覽器更新晝面。

結語

今天的主題雖然蠻簡單的,但應該還算實用,不過最好的解法應該是不要在 useEffect 監聽 state 再去 setState,避免 render 太多次。


右手邊同事的系列文

前端工程師一起來種一棵後端技能樹吧!
想盡辦法當好一個Junior Backend Developer


上一篇
ReactJS 疑難排解:React Fiber
系列文
ReactJS 疑難排解8

1 則留言

0

左手邊的同事加油!

我要留言

立即登入留言