Skip to content

useCallback

作用

返回一个 memoized 回调函数。

把内联回调函数及依赖项数组作为参数传入 useCallback,它将返回该回调函数的 memoized 版本,该回调函数仅在某个依赖项改变时才会更新。当你把回调函数传递给经过优化的并使用引用相等性去避免非必要渲染(例如 shouldComponentUpdate)的子组件时,它将非常有用。

useCallback(fn, deps) 相当于 useMemo(() => fn, deps)

栗子

js
const App = props => {
  // 添加一个 hook
  const [num, setNum] = React.useState(1)
  const [visible, setVisible] = React.useState(true)

  const handleClickBtn = () => {
    setNum(num2 => num2 + 2)
  }
  // 切换visible
  const handleClickChangeVisible = () => {
    setNum(!visible)
  }
  // 打印
  const doSome = React.useCallback(() => {
    console.log("useCallback" + num)
  }, [visible])

  return (
    <div>
      <button onClick={handleClickBtn}>切换Num = {num}</button>
      <button onClick={handleClickChangeVisible}>切换Visible = {visible}</button>
      <button onClick={doSome}>查看doSome打印的内容 </button>
    </div>
  )
}

点击切换num ;num得值在不断的增加;但是这时候如果不点击 切换Visible ;点击doSome的值永远是 useCallback1。

源码分析

仍然分成两个部分: 初次渲染 和 更新渲染 。此处我们一起讲解

js
/**
 * useCallback 在初次渲染执行方法
 * @param {*} callback
 * @param {*} deps
 * @returns
 */
function mountCallback<T>(callback: T, deps: Array<mixed> | void | null): T {
  // 创建 hook
  const hook = mountWorkInProgressHook();
  // 创建 deps
  const nextDeps = deps === undefined ? null : deps;
  // 生成 memoizedState
  hook.memoizedState = [callback, nextDeps];
  return callback;
}
/**
 * useCallback 在更新渲染执行方法
 * @param {*} callback
 * @param {*} deps
 * @returns
 */
function updateCallback<T>(callback: T, deps: Array<mixed> | void | null): T {
  // 找到对应的 hook
  const hook = updateWorkInProgressHook();
  const nextDeps = deps === undefined ? null : deps;
  // 找到旧的memoizedState
  const prevState = hook.memoizedState;
  if (prevState !== null) {
    if (nextDeps !== null) {
      const prevDeps: Array<mixed> | null = prevState[1];
      // 比较新旧deps是否相等
      // 如果不等就 返回新的callback
      if (areHookInputsEqual(nextDeps, prevDeps)) {
        return prevState[0];
      }
    }
  }
  hook.memoizedState = [callback, nextDeps];
  return callback;
}

其跟useState、useEffect 很像,都将信息以hook的形式保存在 FiberNode.memoizedState上。

image

如何只有deps更新的时候才会生成新的callback?

这就是在updateCallback中实现的,其先获取原来的hook,然后通过areHookInputsEqual(nextDeps, prevDeps)判断新旧hook的deps是够发生更新,如果发生更新就返回新的 callback;否则返回旧的prevState[0]。