useCallback和useMemo源码分析

欢迎关注前端早茶,与广东靓仔携手共同进阶

useCallback和useMemo源码分析

前端早茶专注前端,一起结伴同行,紧跟业界发展步伐~

公众号作者:广东靓仔

使用

感觉useCallback和useMemo两者很像,前者返回一个memorized的回调函数,后者返回一个memorized的值。

看一下他们是如何定义的

useCallback接受一个回调函数和依赖项数组作为参数,返回回调函数的memorized版本

// useCallback
useCallback<T>(callback: T, deps: Array<mixed> | void | null): T


当这个回调函数传递给自组件时,可以用useCallback避免自组件非必要的渲染

useMemo接受创建函数和依赖项数组作为参数,会在依赖项发生改变时重新计算memorized值
// useMemo
// 创建函数是有返回值的,这是跟useCallback不同的地方

useMemo<T>(nextCreate: () => T, deps: Array<mixed> | void | null): T


要避免每次渲染都进行高开销的计算时可以用useMemo

小结

1、useCallback(callback, deps) 相当于useMemo(() => fn, deps);
2、deps依赖项数组发生改变时,才会引起useCallbak和useMemo的返回值的更新
3、不传deps时,组件每次渲染他们都会重新计算;
4、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;
}

 

两者从源码看,还是很相似的

欢迎关注前端早茶,与广东靓仔携手共同进阶

useCallback和useMemo源码分析

前端早茶专注前端,一起结伴同行,紧跟业界发展步伐~

公众号作者:广东靓仔

上一篇:文件流转换(一般用于axios设置接收文件流设置时responseType: 'blob')


下一篇:浅谈react hook (useMemo())