第七章: useSyncExternalStore源码解析

前言

这个是关于hook可能导致组件更新的最后一个,前面已经讲了关于能够更新的hook有useState ,useReducer ,useTransition ,useDeferredValue ,实际上useDeferredValue 是没有自己能够主动重新渲染的能力的,依赖于其他方法触发渲染,而其他三种是能够主动触发的;

阅读本章之前建议先读mountEffect,有一些源码是依赖于useEffect的。

源码解析

js 复制代码
// 和useEffect执行时机一致,返回一个函数
const subscribe = (handleStoreChange)=>{
}
// 在createRoot模式下调用
const getSnapshot = () => {
}

// 针对与hydrateRoot模式调用
function getServerSnapshot() {
}
const snapshot = useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot)

mount阶段

按照常规流程,调用HooksDispatcherOnMountInDEV.useSyncExternalstore 的方法,前置check检查方法,核心代码mountSyncExternalStore(subscribe, getSnapshot, getServerSnapshot) ;

首先调用mountWorkInProgressHook方法,建立一个hook对象,

js 复制代码
 const hook = {
    memoizedState: null,
    baseState: null,
    baseQueue: null,
    queue: null,
    next: null
  }

判断当前渲染的模式,是调用getSnapshot 方法还是getServerSnapshot 方法,调用两次*Snapshot方法,将两次返回值进行Object.is比较,如果不同会有个error提示;

js 复制代码
const nextSnapshot = getSnapshot();
hook.memoizedState = nextSnapshot;
const inst = {
    value: nextSnapshot,
    getSnapshot: getSnapshot
}
 hook.queue = inst

调用mountEffect方法,这个是useEffect核心方法,

js 复制代码
function checkIfSnapshotChanged(inst) {
  var latestGetSnapshot = inst.getSnapshot;
  var prevValue = inst.value;
  try {
    var nextValue = latestGetSnapshot();
    return !objectIs(prevValue, nextValue);
  } catch (error) {
    return true;
  }
}
function subscribeToStore(fiber, inst, subscribe) {
  var handleStoreChange = function () {
    if (checkIfSnapshotChanged(inst)) {
      // Force a re-render.
      forceStoreRerender(fiber);
    }
  }; 
  return subscribe(handleStoreChange);
}
mountEffect(subscribeToStore.bind(null, fiber, inst, subscribe), [subscribe]);
const Passive = 0b00000000000000100000000000;
fiber.flags |= Passive;

关于mountEffect的代码会放在mountEffect章节中讲解; 主要是创建一个hook对象,和调用pushEffect 方法,

本身也会调用一次pushEffect方法,

js 复制代码
const HasEffect = 0b0001;
const Passive = 0b1000;
function updateStoreInstance<T>(
  fiber,
  inst,
  nextSnapshot,
  getSnapshot,
) {
  inst.value = nextSnapshot;
  inst.getSnapshot = getSnapshot;
  if (checkIfSnapshotChanged(inst)) {
    // Force a re-render.
    forceStoreRerender(fiber);
  }
}
pushEffect(
    HasEffect | Passive, // flags状态
    updateStoreInstance.bind(null, fiber, inst, nextSnapshot, getSnapshot),
    undefined,
    null,
);

nextSnapshot返回。

fiber.updateQueue的任务队列中会创建两个链表,一个是mountEffect创建的,一个是pushEffect创建的;

在执行useEffect时机时,参考commitRoot阶段,遍历updateQueue队列,首先执行mountEffect任务,会执行subscribe 方法,传入一个函数handleStoreChange , 然后执行pushEffect任务,执行updateStoreInstance函数,会再次调用getSnapshot方法,对比之前的值,决定是否调用forceStoreRerender方法;

调用handleStoreChange方法

调用handleStoreChange 更新方法,就是调用subscribe中的行参的方法,内部调用getSnapshot方法,将上次和这次的返回值对比,如果不相同就进行重新渲染;

update 阶段

调用HooksDispatcherOnUpdateInDEV.useSyncExternalStore 方法,调用check方法;

调用updateWorkInProgressHook方法,浅copy hook对象,调用两次getSnapshot方法,对比返回值,如果不同会报错,将getSnapshot返回结果更新到hook.memoizedState(上一次调用getSnapshot返回值);

js 复制代码
updateEffect(subscribeToStore.bind(null, fiber, inst, subscribe), [subscribe])

updateEffect是useEffect更新阶段核心代码,在mountEffect更新部分中讲解;

同样更新pushEffect部分的hook

js 复制代码
fiber.flags |= Passive;
pushEffect(
  HookHasEffect | HookPassive,
  updateStoreInstance.bind(null, fiber, inst, nextSnapshot, getSnapshot),
  undefined,
  null,
);

返回最新的nextSnapshot

同样在更新阶段执行useEffect时机时,参考useEffect中update阶段判断,也是在当前任务队列中执行;

根据updateEffect的核心代码,判断依赖项subscribe是否有改变,没有的话就不执行getSnapshot和返回函数,继续执行pushEffect创建的updateStoreInstance的函数,对比getSnapshot和返回值和上次的值,判断是否需要更新;

总结

目前还没找到很好的单独应用实例,据说,我听说最新版的redux就是使用useSyncExternalStore。

相关推荐
yanyu-yaya2 分钟前
第四章 react-redux,@reduxjs/toolkit依赖,学习
前端·学习·react.js
小付同学呀7 分钟前
前端快速入门学习3——CSS介绍与选择器
前端·css·学习
Jenlybein7 分钟前
快速了解浏览器原理及工作流程
前端·浏览器
醋醋8 分钟前
vue2源码记录(2)
前端·vue.js
前端飞天猪8 分钟前
学习笔记:企业级Git代码规范与协作指南💖
前端·github
艾克马斯奎普特8 分钟前
Vue.js 3 渐进式实现之响应式系统——第五节:分支切换与 cleanup
前端·vue.js
总之就是非常可爱10 分钟前
五分钟看懂 alien signals 依赖收集原理
前端·vue.js
Bruce_Liuxiaowei11 分钟前
基于Flask的Windows命令大全Web应用技术解析与架构设计
前端·windows·python·flask
EricXJ12 分钟前
npm、Yarn、pnpm Workspace 对比
前端·javascript
DoraBigHead15 分钟前
🔬 深度解析:前端异步模型的本质机制与工程落点
前端