生命週期的味道。
簡述
React render 完,瀏覽器 paint 之前,你想做什麼?
一樣是 todo list 的範例,假設我在讀 loaclStorage 資料的時候是這樣寫的:
1 | function App() { |
這時候就會出現「閃一下」的問題,像這樣:

(我電腦寫不出這種效果,可能是閃太快的關係吧,所以改貼 Lidemy 的影片內容)
這是因為執行順序是這樣:
- 第一次 render,瀏覽器 paint 初始值的
todos - 執行
useEffect,讀取loaclStorage,更新todos的 state - state 變了所以再 render 一次,瀏覽器再 paint 成新的
todos
這就是為什麼會「閃一下」,因為 useEffect 是在瀏覽器 paint 完以後才會被執行的 hook,所以一開始會先顯示預設的 todos,接著執行 useEffect 時改了 state,而 state 改變後又重新 render 了一次畫面,最後顯示新的 todos。
可以參考這張圖,大概理解一下 hook 的生命週期

總之一定要知道 useEffect 是讚 paint 完以後才會被執行就對了。
回到一開始的問題,那要怎麼解決才好?
從上面的圖你應該能注意到一個東西是「Run LayoutEffects」,看起來就是在說它是跑在「Browser paints screen」以前的 hook,那麼是不是用這個就可以解決了?
沒錯,正是如此哦!
1 | useLayoutEffect(() => { |
改成這樣以後的執行順序就會變成:
- 第一次 render,進入瀏覽器 paint 以前的 hook
- 執行
useLayoutEffect,讀取localStorage,更新todos的 state - state 變了所以再 render 一次,瀏覽器再把
todos給 paint 出來
因為是在瀏覽器把畫面 paint 出來以前就先更新了 state,所以原本的 state 在被 render 出來之前就被覆寫掉了,因此最後 paint 時才會是新的 state。(不懂的話就看著圖,想想看每一步的流程應該就會懂了)