iT邦幫忙

第 12 屆 iT 邦幫忙鐵人賽

DAY 5
0
Modern Web

ReactJS 疑難排解系列 第 5

ReactJS 疑難排解:為什麼用了 React.memo 還是一直 re-render

我們先來看以下兩個例子,想想當 <Parent/> re-render 時,Child 會不會也跟著 re-render 呢?

function Child({ data }) {
  return <div>{data.a}</div>;
}
const MemoChild = React.memo(Child);

function Parent() {
  const data = { a: 1, b: 2 };
  
  return <MemoChild data={data}/>;
}
function Child({ onClick }) {
  return <button onClick={onClick}>click</button>;
}
const MemoChild = React.memo(Child);

function Parent() {
  function onClick() {
    console.log('hello world!');
  }
  
  return <MemoChild onClick={onClick}/>;
}

這兩題的答案都是肯定的,明明都包上了 React.memo 但還是沒用,到底是為什麼呢
讓我們先來看看 React.memo 到底做了哪些事

React.memo

Docs
React.memo 是一個 HOC,當 props update 的時候會去 invoke 第二個參數 (areEqual[func]) 來判斷要不要進行 re-render。

要注意的是他只有負責監聽 props 的改變,這點和傳統的 shouldComponentUpdate 有些許不同。 另外,預設的 areEqual function 是 shallowCompare,因此上面兩個例子中,才會被判斷成需要 re-render。

這樣看起來用了 React.memo 反而會增加許多無謂的比較,那到底該不該用 React.memo
Dan Abramov 的這則推文也曾經提到,大多數的 component 理當不會一直收到相同的 props,因此以上題為例,我們應該是要避免發生 Parent 無故 re-render 的行為。

從 Shallow Compare 出發

因為 shallow compare 只有 primitive type 會去比較 value(===),其他都是看 reference 的。
所以useMemouseCallback 就派上用場了。

UseMemo

Docs
回傳一個 memoized 的值。

傳遞一個「建立」function 及依賴 array。useMemo 只會在依賴改變時才重新計算 memoized 的值。這個最佳化可以避免在每次 render 都進行昂貴的計算。

以上題為例:

  // const data = { a: 1, b: 2 };
  const data = useMemo(() => ({ a: 1, b: 2 }), []);

UseCallback

Docs
回傳一個 memoized 的 callback。

傳遞一個 inline callback 及依賴 array。useCallback 會回傳該 callback 的 memoized 版本,它僅在依賴改變時才會更新。當傳遞 callback 到已經最佳化的 child component 時非常有用,這些 child component 依賴於引用相等性來防止不必要的 render(例如,shouldComponentUpdate)

以上題為例:

  /**
   *  function onClick() {
   *   console.log('hello world!');
   *  }
   */
   const onClick = useCallback(function() {
     console.log('hello world!');
   }, []);

如此一來,即使 <Parent/> re-render,<Child/> 也不會有所影響了。

結語

雖說使用這兩個 hooks 可以解決上述 re-render 的問題,但重點是這值不值得呢?

不光原先的 React.memo 會造成 O(props.length) 的比較,額外為了一個 object literial 來使用 useMemo 究竟划不划算了,我想這就見仁見智了。畢竟也沒有明確的 benchmark 來顯示說該不該使用 useMemouseCallback 來包裝要傳進去的 props。

另外,接下來幾天應該都會分享關於 render 相關的議題,喜歡的可以發摟一下喔~


上一篇
ReactJS 疑難排解:會報錯的 console.log(event)
下一篇
ReactJS 疑難排解:什麼是 reconciliation
系列文
ReactJS 疑難排解8

尚未有邦友留言

立即登入留言