useMemo模拟useCallback

useMemo缓存结果,useCallback缓存函数

1、看一下useMemo和useCallback底层源码的区别

useMemo 源码实现

组件首次渲染时 useMemo 的源码实现:

// React 版本:16.13.1
// react-reconciler/src/ReactFiberHooks.new.js
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; // 返回缓存后的变量值
}

useMemo 的依赖项发生变化时useMemo的源码实现:

// React 版本:16.13.1
// react-reconciler/src/ReactFiberHooks.new.js
function updateMemo<T>(
  nextCreate: () => T,
  deps: Array<mixed> | void | null,
): T {
  const hook = updateWorkInProgressHook();
  const nextDeps = deps === undefined ? null : deps;
  const prevState = hook.memoizedState;
  if (prevState !== null) {
    // Assume these are defined. If they're not, areHookInputsEqual will warn.
    if (nextDeps !== null) {
      const prevDeps: Array<mixed> | null = prevState[1];
      if (areHookInputsEqual(nextDeps, prevDeps)) {
        return prevState[0];
      }
    }
  }
  const nextValue = nextCreate();
  hook.memoizedState = [nextValue, nextDeps];
  return nextValue;
}

依赖对比:

function areHookInputsEqual(nextDeps, prevDeps) {
  if (prevDeps === null) {
    {
      warning$1(false, '%s received a final argument during this render, but not during ' + 'the previous render. Even though the final argument is optional, ' + 'its type cannot change between renders.', currentHookNameInDev);
    }
    return false;
  }

  {
    // Don't bother comparing lengths in prod because these arrays should be
    // passed inline.
    if (nextDeps.length !== prevDeps.length) {
      warning$1(false, 'The final argument passed to %s changed size between renders. The ' + 'order and size of this array must remain constant.\n\n' + 'Previous: %s\n' + 'Incoming: %s', currentHookNameInDev, '[' + nextDeps.join(', ') + ']', '[' + prevDeps.join(', ') + ']');
    }
  }
  for (var i = 0; i < prevDeps.length && i < nextDeps.length; i++) {
    if (is(nextDeps[i], prevDeps[i])) {
      continue;
    }
    return false;
  }
  return true;
}


/**
 * inlined Object.is polyfill to avoid requiring consumers ship their own
 * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is
 */
function is(x, y) {
  return x === y && (x !== 0 || 1 / x === 1 / y) || x !== x && y !== y // eslint-disable-line no-self-compare
  ;
}

注意:上面react包括state和props对比都是采用shallowEaque,只比较一层。且依赖是对象时X===Y不同对象即使内容一样仍然return false,如下案例,除非前两种useMemo才不会更新,否则每次都更新,useEffect及useCallback同理,useMemo常用来缓存组件,所以不要乱用useMemo等hooks

const [obj, setObj] = useState({a:1})



//如点击等导致state更新
setObj(obj)
setObj(preObj=>preObj)
setObj(preObj=>{...preObj})

useMemo(()=>{....},[obj]

useCallback 源码实现

组件首次渲染时 useCallback 的源码实现:

// React 版本:16.13.1
// react-reconciler/src/ReactFiberHooks.new.js
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;
}

useCallback 的依赖项发生变化时useMemo的源码实现:

// React 版本:16.13.1
// react-reconciler/src/ReactFiberHooks.new.js
function updateCallback<T>(callback: T, deps: Array<mixed> | void | null): T {
  const hook = updateWorkInProgressHook();
  const nextDeps = deps === undefined ? null : deps;
  const prevState = hook.memoizedState;
  if (prevState !== null) {
    if (nextDeps !== null) {
      const prevDeps: Array<mixed> | null = prevState[1];
      if (areHookInputsEqual(nextDeps, prevDeps)) {
        return prevState[0];
      }
    }
  }
  hook.memoizedState = [callback, nextDeps];
  return callback;
}

useMemo 和 useCallback 都是返回一个缓存后的值,useMemo 返回的是一个缓存后的变量,而useCallback 返回的是一个缓存后的回调函数。可以在 useMemo 的 "创建函数" 中返回一个回调函数来模拟 useCallback 的功能。useMemo 和 useCallback 可以用来解决函数组件更新过程中的性能问题。

// 使用 useCallback 缓存函数
  const addClick = useCallback(() => {
    let sum = 0;
    for (let i = 0; i < count; i++) {
      sum += i;
    }
    return sum;
  }, [count]);



// 使用 useMemo替代useCallback 缓存函数
  const addClick = useMemo(() => () => {
    let sum = 0;
    for (let i = 0; i < count; i++) {
      sum += i;
    }
    return sum;
  }, [count]);

猜你喜欢

转载自blog.csdn.net/CamilleZJ/article/details/125452929
今日推荐