React Hook之useCallback和useMemo的使用和源碼分析

如何使用

感覺useCallback和useMemo兩者很像,前者返回一個memorized的回調函數,後者返回一個memorized的值。

看一下他們是如何定義的

  1. useCallback接受一個回調函數和依賴項數組作爲參數,返回回調函數的memorized版本
// useCallback
useCallback<T>(callback: T, deps: Array<mixed> | void | null): T

當這個回調函數傳遞給自組件時,可以用useCallback避免自組件非必要的渲染


  1. useMemo接受創建函數和依賴項數組作爲參數,會在依賴項發生改變時重新計算memorized值
// useMemo
// 創建函數是有返回值的,這是跟useCallback不同的地方
useMemo<T>(nextCreate: () => T, deps: Array<mixed> | void | null): T

要避免每次渲染都進行高開銷的計算時可以用useMemo

綜上:

  • useCallback(callback, deps) 相當於useMemo(() => fn, deps);
  • deps依賴項數組發生改變時,纔會引起useCallbak和useMemo的返回值的更新
  • 不傳deps時,組件每次渲染他們都會重新計算;
  • deps傳空數組[]時,只有組件首次加載會計算;

源碼分析

看源碼,兩者也是很相似的
還是老套路,首次掛載組件時,走的時mount**, 組件更新時走的是update**
useCallback和useMemo的源碼部分比較相似和簡單。

// mount階段就是獲取到傳入的回調函數和依賴數組,保存到hook的memorizedState中,然後返回回調函數。
function mountCallback<T>(callback: T, deps: Array<mixed> | void | null): T {
  const hook = mountWorkInProgressHook();
  const nextDeps = deps === undefined ? null : deps;
  hook.memoizedState = [callback, nextDeps];
  return callback;
}

// update階段
function updateCallback<T>(callback: T, deps: Array<mixed> | void | null): T {
  const hook = updateWorkInProgressHook();
 // 從hook的memorizedState中獲取上次保存的值[callback, deps],
  const nextDeps = deps === undefined ? null : deps;
  const prevState = hook.memoizedState;
  if (prevState !== null) {
    if (nextDeps !== null) {
      const prevDeps: Array<mixed> | null = prevState[1];
      // 比較新的deps和之前的deps是否相等
      if (areHookInputsEqual(nextDeps, prevDeps)) {
      // 如果相等,返回memorized的callback
        return prevState[0];
      }
    }
  }
  // 如果deps發生變化,更新hook的memorizedState,並返回最新的callback
  hook.memoizedState = [callback, nextDeps];
  return callback;
}

useMemo的源碼如下:

// mount階段, 執行創建函數獲得返回值
// 保存到hook的memorizedState中[nextValue, nextDeps]
function mountMemo<T>(
  nextCreate: () => T,
  deps: Array<mixed> | void | null,
): T {
  const hook = mountWorkInProgressHook();
  const nextDeps = deps === undefined ? null : deps;
  const nextValue = nextCreate();
  hook.memoizedState = [nextValue, nextDeps];
  return nextValue;
}

// update階段
function updateMemo<T>(
  nextCreate: () => T,
  deps: Array<mixed> | void | null,
): T {
  const hook = updateWorkInProgressHook();
  // 獲取新的deps
  const nextDeps = deps === undefined ? null : deps;
  // 從memorizedState中獲得上次保存的值
  const prevState = hook.memoizedState;
  if (prevState !== null) {
    if (nextDeps !== null) {
    // 比較新deps和舊deps是否相等,如果兩者相等,返回舊的創建函數的返回值
      const prevDeps: Array<mixed> | null = prevState[1];
      if (areHookInputsEqual(nextDeps, prevDeps)) {
        return prevState[0];
      }
    }
  }
  // 如果deps發生改變,hook中保存新的返回值和deps,並返回新的創建函數的返回值
  const nextValue = nextCreate();
  hook.memoizedState = [nextValue, nextDeps];
  return nextValue;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章