iT邦幫忙

2021 iThome 鐵人賽

DAY 30
0
自我挑戰組

30天CSS、JS、React打造專案零組件系列 第 30

DAY30 - [React] useMemo 與 後續

今日文章目錄

  • 前言
  • useMemo()
  • 實作紀錄
  • 參考資料
  • 後續

今天要練習 useMemo(),不過爬了很多文,覺得還無法好好消化,這篇主要紀錄一些我找到的資料筆記,還有練習的過程。開始吧!

前言

昨天我們在切換頁面的時候,透過Provider value props把{state, setState}傳下去,讓子層可以改值。

// Provider 傳值與方法:
function App() {
  const [themsState, setThemeState] = useState(theme.light);

  return (
    <ThemeContext.Provider value={{ themsState, setThemeState }}>
    ...
    </ThemeContext.Provider>
  );
}

export default App;
function CategoryList(props) {
   const { themsState, setThemeState } = useContext(ThemeContext);
   return (...)
}

console.log()看一下,會發現:Provider傳什麼,useContext()就會回傳什麼(就是最新值)。
Provider傳什麼,useContext()就會回傳什麼(就是最新值)

官方文件有提到以下情況:
Caveats

我想確定目前寫法:<Context.Provider value ={{state, setState}}></Context.Provider>會不會造成上述的問題。搜了一輪資料,決定用useMemo inside Context API - React的方法來測試看看。

為了做個測試,我分別在父層ToDoPage,子層CategoryList TodoList component內加入 console.log(),來確認一下使用useMemo()與沒有使用useMemo()的差異。


測試目標:當我在 ToDoPage點擊按鈕更新值時,Provider props value 是否會重新建立,導致子層 CategoryList ToDoList re-render?

  • 對比項目:
    • CategoryList 父層加入 ThemeContext.Provider value props :
      使用 useMemo(),當 dependency改變時,才更新回傳值。
    • ToDoList 父層加入 ThemeContext.Provider value props :
      直接傳入{ themsState, setThemeState }

測試A

測試A

export default function ToDoPage() {
  const [forceRender, setForceRender] = useState(false);
  const [themsState, setThemeState] = useState(theme.light);
  const themeContextProvider = useMemo(() => (
      { themsState, setThemeState }), [themsState, setThemeState]
  );

  return (
    <>
      {console.log('hey! I am in ToDoPage.')}
      <MainLayout
        menu={(
          <ThemeContext.Provider value={themeContextProvider}>
            <CategoryList />
          </ThemeContext.Provider>
        )}
        content={(
          <ThemeContext.Provider value={{ themsState, setThemeState }}>
            <ToDoList />
          </ThemeContext.Provider>
        )}
      />
      <Button onClick={() => setForceRender(!forceRender)}>Rerender Parent</Button>
    </>
  );
}

顯示效果:

  • ToDoListCategoryList 切換 theme,更新ThemeContext.Provider值。
    ToDoList 或 CategoryList 切換 theme,更新ThemeContext.Provider值。

  • ToDoPage 點擊按鈕: 觸發 ToDoPage CategoryList ToDoList re-render。
    ToDoList 或 CategoryList 切換 theme,更新ThemeContext.Provider值。

疑?看不出來 useMemo() 的效果


測試B :

測試B

  • CategoryList、ToDoList 加入 memo()
    試試看!
export default memo(CategoryList);
export default memo(ToDoList);

顯示效果:

  • ToDoListCategoryList 切換 theme,更新ThemeContext.Provider值。
    ToDoList 或 CategoryList 切換 theme,更新ThemeContext.Provider值。

  • ToDoPage 點擊按鈕: 觸發 ToDoPage ToDoList re-render。CategoryList 沒有。
    ToDoPage 點擊按鈕: 觸發 ToDoPage ToDoList re-render。CategoryList 沒有

呼~終於有點收穫...

目前看起來的狀況:
測試A:父層狀態改變,子層re-render。
測試B:父層狀態改變,透過memo()比對CategoryList ToDoList,根本沒有props,所以不受父層狀態改變影響,唯一帶來改變的是ToDoListThemeContext.Provider value={{ themsState, setThemeState }},每次都重新建立。

不過我應該把 console.log() 放一個在 useMemo()比較準才對..


useMemo 小百科:

useMemo(() => computeExpensiveValue(a, b), [a, b]);

  • 為了解決當元件本身改變狀態 re-render,避免元件內無關的邏輯重複渲染的效能問題。
  • 用途:當dependency改變,才會執行computeExpensiveValue(a, b)重新計算,回傳一個 memoized 的值。
  • 參數:
  • computeExpensiveValue(a, b) : 一function,回傳值會讓React記著,在dependency沒改變的情況下,會一直沿用該回傳值。
  • [a, b]: dependency。

memo 小百科:

memo(MyComponent, areEqual) :
React 渲染機制,只要父層state改變,不論子層接收的props值是否有改變,子層一律強迫 re-render。
memo()是為了解決子層接收的props導致重複渲染的效能問題。

  • 用途:比對 更新前後props差異,沒有改變,會跳過這次 re-render。
  • 參數:
    > MyComponent : 作用元件
    > areEqual *(optional)* : 客制function。memo()比對方式 shallowly compare complex objects in the props object,如果要做更深層的比對,可以自己寫。
  • If your function component wrapped in React.memo has a useState, useReducer or useContext Hook in its implementation, it will still rerender when state or context change. - 節錄React 官網。

雜記

  • useXXX comparison: Object.is(value1, value2)

React uses the Object.is comparison algorithm.


參考資料


後續

哇~真的把30天撐完了,好感動喔
10天React練習讓我有機會重新再回來看React 文件,我記得我剛開始接觸的時候,真的是有看沒有懂...目前寫了半年多,回來看文件會發現以前看不懂的,現在漸漸能理解它的意思,還有很多需要再練習,但覺得滿足。感謝鐵人賽!

這10天只有寫到一小部分,回頭看自己的文章,有些知識漏洞。之後會繼續補齊 use系列,不會一天一po,等我練到有點心得再繼續跟大家分享,恭喜各位鐵人完賽,下次見囉!


上一篇
DAY29 - [React] useContext 實作篇
系列文
30天CSS、JS、React打造專案零組件30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言