源码分析之React中useCallback和useMemo

概述

在React中,useCallbackuseMemo分别用于缓存函数和变量,当其依赖没发生改变时,可以复用旧的函数或变量,一定程度上可以优化函数组件的性能。

源码分析

useCallback

  • 挂载(首次渲染)

函数组件在挂载首次渲染时,useCallback调用的方法是mountCallback

js 复制代码
 // callback 表示回调函数,deps是依赖数组,下同
function mountCallback(callback, deps) {
 // 调用 mountWorkInProgress方法创建新的hook,并将其放到fiber的memoizedState链表上,最后返回新的hook
  const hook = mountWorkInProgressHook();
  // 判断依赖,若不存在,则为null
  const nextDeps = deps === undefined ? null : deps;
  // 将callback和deps 存放到fiber.memoizedState中,以便后续复用
  hook.memoizedState = [callback, nextDeps];
  // 最后返回callback
  return callback;
}
  • 组件更新时

函数组件在更新时,hook 的分发器dispatch 会将useCallback指向updateCallback

其源码实现如下:

js 复制代码
function updateCallback(callback, deps) {
  // 调用updateWorkInProgressHook读取旧Fiber的对应旧hook,以及复用旧hook,创建新hook并返回
  const hook = updateWorkInProgressHook();
  // 新的依赖取值
  const nextDeps = deps === undefined ? null : deps;
  // 从新hook上取hook之前数据
  const prevState = hook.memoizedState;
  // 若新依赖不等于null
  if (nextDeps !== null) {
    const prevDeps = prevState[1];
    // 则判断新旧依赖是否发生了改变
    if (areHookInputsEqual(nextDeps, prevDeps)) {
    // 若没变化,则直接返回之前的callback
      return prevState[0];
    }
  }
  // 若新依赖不存在,或者新旧依赖不等,则再次将callback和deps依赖存入到fiber中
  hook.memoizedState = [callback, nextDeps];
  // 返回callback
  return callback;
}

useMemo

useMemo缓存的是回调函数的返回值,useCallback缓存的是回调函数,因此useMemo不过就是比useCallback的回调函数执行一次,并将其结果存到fiber.memoizedState的链表中,并返回;后续更新调度时,若依赖没发生改变,就读取旧hook中的值;若发生改变,再次执行回调函数,将它的返回值存起来,同时返回即可。

其源码实现如下:

js 复制代码
function mountMemo(
  nextCreate,
  deps,
): T {
  const hook = mountWorkInProgressHook();
  const nextDeps = deps === undefined ? null : deps;
  const nextValue = nextCreate();

  hook.memoizedState = [nextValue, nextDeps];
  return nextValue;
}

function updateMemo(
  nextCreate,
  deps,
) {
  const hook = updateWorkInProgressHook();
  const nextDeps = deps === undefined ? null : deps;
  const prevState = hook.memoizedState;
  
  if (nextDeps !== null) {
    const prevDeps = prevState[1];
    if (areHookInputsEqual(nextDeps, prevDeps)) {
      return prevState[0];
    }
  }
  const nextValue = nextCreate();

  hook.memoizedState = [nextValue, nextDeps];
  return nextValue;
}

总结

useCallbackuseMemo就是将函数和依赖存放在fiber.meomizedState的链表中。在函数组件重新渲染后,若依赖没有变化,就复用之前的旧值。若useRef这个hook 的值不与DOM绑定,只是在函数组件内部使用,在函数组件更新后,也是复用旧值。它们的原理类似。

相关推荐
前端甜糖2 分钟前
记录We码开发者工具的一个bug
前端·javascript
程序员黑豆11 分钟前
鸿蒙应用开发教程:以红绿灯切换为例,掌握条件渲染的核心用法
前端·harmonyos
猫32819 分钟前
echarts 日常问题解决
前端·javascript·echarts
新中地GIS开发老师22 分钟前
地信职业百科④:GIS开发工程师
前端·数据库·gis·webgis·三维gis开发
我是大卫31 分钟前
【图】React源码解析-从数据结构、调度器、源码设计模式到状态计算引擎,深挖useReducer原理
前端·react.js·源码
willes38 分钟前
列表中的倒计时组件
前端
LVZ40 分钟前
用了半年 AI 写代码,最烦的不是代码,是环境
前端·后端·开源
polaris_tl40 分钟前
一行 `compress: false`,为什么让 SSE 首包恢复实时返回
前端
海边的云42 分钟前
别再让 AI 瞎改代码:一套让大模型「收敛」的前端专家 Skill》
前端
kisshyshy42 分钟前
给端侧大模型装上“发动机”:React 合成事件 + 进度条组件全解
前端·react.js·node.js