iT邦幫忙

2024 iThome 鐵人賽

DAY 24
0
佛心分享-刷題不只是刷題

30 天克服前端面試系列 第 24

Day 24 - useEffect dependencies 機制設計的目的? dependencies 的不同情境運作?

  • 分享至 

  • xImage
  •  

useEffect hook 最主要的作用在於處理與畫面無關的 side effect,並非是 functional component 的生命週期API,因為這個處理副作用的 hook 不論呼叫幾次都不應該影響資料流或是程式邏輯。

useEffect 的 dependencies 機制設計的目的

useEffect 的 dependencies 機制設計的目的是為了讓 React 能夠正確地同步資料,並且優化效能。
在 dependencies 中傳入 side effect 函式所需要依賴的資料,react 就會在每一個渲染時使用
object.is() 來比較前後兩次的 dependencies 依賴的原始資料否有變化,若有變化就會執行 side effect 函式,沒有就可以跳過這次 side effect 的執行。

dependencies 並非是為了控制 side effect 執行的時機或商業邏輯,而是為了讓 react 可以正確地同步化資料,所以應誠實地填寫有依賴到的原始資料到 dependencies 中。

在 useEffect 中,根據 dependencies 的不同情境運作的方式:

dependencies 為空陣列

import React, { useEffect, useState } from "react";
function App() {
  useEffect(() => {
    console.log("Component rendered");
  }, []);
  return <div>My Component</div>;
}

運作流程:

  • render:

    • render 階段:react 執行 App component function 產出 react element。
    • commit 階段:瀏覽器實際的 DOM 中並沒有相對應的 component 實例 DOM element,因此會將 react element 全部轉成相對應的實際 DOM element。然後使用瀏覽器的 DOM API appendChild() 將生成的 DOM element 掛載到實際的瀏覽器 DOM 上。
    • mount: 執行 side effect,印出 "Component rendered"。
  • re-render: 假設之後因畫面更新觸發 re-render,由於 dependencies 為空陣列,react 會比較前後兩次的 dependencies 依賴的原始資料,發現沒有變化,因此不會執行 side effect。

dependencies 中傳入 useState

    import React, { useEffect, useState } from "react";
    function App() {
      const [count, setCount] = useState(0);
      useEffect(() => {
        console.log("Component rendered");
      }, [count]);
      return (
        <div>
          <button onClick={() => setCount((prevCount)=>prevCount+1)}>Click me</button>
          <div>{count}</div>
        </div>
      );
    }

運作流程:

  • render:
    • render 階段:react 執行 App component function 產出 react element。
    • commit 階段:瀏覽器實際的 DOM 中並沒有相對應的 component 實例 DOM element,因此會將 react element 全部轉成相對應的實際 DOM element。然後使用瀏覽器的 DOM API appendChild() 將生成的 DOM element 掛載到實際的瀏覽器 DOM 上。
    • mount: 執行 side effect,印出 "Component rendered"。
  • re-render:
    • 當點擊 button 時觸發 setCount((prevCount)=>prevCount+1),count 的值更新到 1。
    • object.is() 檢查前後兩次的 count 值,發現有變化,因此進入 reconciliation 階段,重新 render。
    • render 階段:react 執行 App component function 產出 react element。
    • commit 階段: 將前後兩次的 react element 進行樹狀結構的比較,找出差異,並且只會去操作新舊 react element 差異所對應的實際 DOM element。
    • mount : 透過 object.is()檢查 useEffect dependencies 中所有的項目,發現所依賴的資料 count 值有變化,因此執行 side effect,印出 "Component rendered"。

不傳入 dependencies

import React, { useEffect, useState } from "react";
function App() {
  const [count, setCount] = useState(0);
  useEffect(() => {
    console.log("Component rendered");
  });
  return (
    <div>
      <button onClick={() => setCount((prevCount)=>prevCount+1)}>Click me</button>
      <div>{count}</div>
    </div>
  );
}

運作流程:

  • render:

    • render 階段:react 執行 App component function 產出 react element。
    • commit 階段:瀏覽器實際的 DOM 中並沒有相對應的 component 實例 DOM element,因此會將 react element 全部轉成相對應的實際 DOM element。然後使用瀏覽器的 DOM API appendChild() 將生成的 DOM element 掛載到實際的瀏覽器 DOM 上。
    • mount: 執行 side effect,印出 "Component rendered"。
  • re-render: 假設之後因畫面更新觸發 re-render,沒有傳入 dependencies,因此沒有參考的依賴資料,只要重新渲染就會執行一次 side effect,印出 "Component rendered"。

總結:

  • 空陣列:useEffect 只在初次 mount 時執行一次,不會再執行。
  • 傳入依賴項:依賴項改變時執行副作用函式,狀態變化正確同步。
  • 無依賴項:每次渲染都會執行副作用,這可能會導致不必要的效能問題。

本文同步於此


上一篇
Day 23 - React 的 useEffect 是什麼?如何使用?
下一篇
Day 25 - useCallback 和 useMemo 的用途和使用時機
系列文
30 天克服前端面試30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言