跳到主要内容

认识

2024年03月01日
柏拉文
越努力,越幸运

一、认识


useDeferredValue 用于在 createRoot 开启的并发特性模式下, 开启并发渲染。主要解决浏览器执行大量渲染工作问题时的卡顿问题。useDeferredValue 传入一个可能会引发大量渲染工作的状态, 返回一个新的状态。useDeferredValue 把传入的状态通过过渡任务得到新的值,这个值作为延时状态。

1.1 useTransition vs useDeferredValue

useDeferredValue 本质上和内部实现与 useTransition 一样都是标记成了过渡更新任务。但是, useTransition 是把 startTransition 内部的更新任务变成了过渡任务 transition,而 useDeferredValue 是把原值通过过渡任务得到新的值,这个值作为延时状态。 一个是处理一段逻辑,另一个是生产一个新的状态。useDeferredValue 还有一个不同点就是这个任务,本质上在 useEffect 内部执行,而 useEffect 内部逻辑是异步执行的 ,所以它一定程度上更滞后于 useTransitionuseDeferredValue = useEffect + transition

二、细节


2.1 hook

const hook = {
next: null,
baseQueue: null,
baseState: null,
updateQueue: null,
memoizedState: null
};

2.2 update

export const createUpdate = (
action,
lane,
hasEagerState = false,
eagerState = null
) => {
return {
lane,
action,
next: null,
hasEagerState,
eagerState
};
};

2.3 updateQueue

function createFCUpdateQueue() {
const updateQueue = createUpdateQueue();
updateQueue.lastEffect = null;
return updateQueue;
}
export const createUpdateQueue = () => {
return {
shared: {
pending: null
},
dispatch: null
};
};

updateQueue.shared.pending: 存储 update 对象

updateQueue.dispatch: 存储 dispatchAction.bind() 的值

2.4 hook.updateQueue

hook.updateQueue 用于存储 updateQueue

2.5 hook.memoizedState

hook.memoizedState 用于存储 state

2.6 fiber.memoizedState

fiber.memoizedState 用于存储 hooks 链表

三、执行函数组件


React.jsbeginWork 阶段, 遇到 FunctionComponent 类型的 Fiber, 会执行 updateFunctionComponentupdateFunctionComponent 内部调用 renderWithHooks。主要工作如下:

  1. 重置状态: 函数组件执行前, 重置当前正在处理的 hook、当前正在使用的 hook

  2. 记录当前组件 fiber: 通过 currentlyRenderingFiber 变量记录当前组件 fiber

  3. 选择对应阶段的 Hook: mount 阶段选择 HooksDispatcherOnMount 作为 Hooks Map, update 阶段选择 HooksDispatcherOnUpdate 作为 Hooks Map函数组件mount 阶段 创建 hook 数据结构, 存储到 fiber.memoizedState 或者 hook.next, 初次建立其 hooksfiber 之间的关系。函数组件 update 阶段 找到当前函数组件 fiber, 找到当前 hook, 更新 hook 状态。

  4. 执行函数组件: 执行我们真正函数组件,所有的 hooks 将依次执行, 每个 hook 内部要根据 currentlyRenderingFiber 读取对应的内容,

  5. 重置状态: 执行完 Component, 立马重置 currentlyRenderingFiber, 防止函数组件外部调用。置当前正在处理的 hook、当前正在使用的 hook

3.1 /packages/react-reconciler/fiberHooks.js

export function renderWithHooks(workInProgress, Component, lane) {
renderLane = lane;
currentlyRenderingFiber = workInProgress;
workInProgress.memoizedState = null;
workInProgress.updateQueue = null;

const current = workInProgress.alternate;

if (current !== null) {
// update
currentDispatcher.current = HooksDispatcherOnUpdate;
} else {
// mount
currentDispatcher.current = HooksDispatcherOnMount;
}

const props = workInProgress.pendingProps;
const children = Component(props);

currentHook = null;
renderLane = NoLane;
workInProgressHook = null;
currentlyRenderingFiber = null;

return children;
}

3.2 /packages/react-reconciler/fiberHooks.js

/**
* @description: Mount 阶段 Hooks 实现
*/
const HooksDispatcherOnMount = {
use,
useRef: mountRef,
useMemo: mountMemo,
useState: mountState,
useEffect: mountEffect,
useContext: readContext,
useCallback: mountCallback,
useTransition: mountTransition
};

/**
* @description: Update 阶段 Hooks 实现
*/
const HooksDispatcherOnUpdate = {
use,
useRef: updateRef,
useMemo: updateMemo,
useState: updateState,
useEffect: updateEffect,
useContext: readContext,
useCallback: updateCallback,
useTransition: updateTransition
};

四、函数组件 mount 阶段 Hook


useTransition 通过调用 mountState 基于传入的状态维护一个新的状态, 在 useEffect 中, 依赖传入状态的变化, 更新新的状态。更新新的状态之前, 会更新全局配置 currentBatchConfig.transitiontrue, 表示当前任务为过度任务。更新新的状态过程中会触发更新任务, 此时获取的任务优先级时, 会判断 currentBatchConfig.transition, 如果为 true, 那么此时的更新任务优先级就是 过渡任务优先级。更新新的状态完毕后, 将全局配置 currentBatchConfig.transition 恢复到之前状态。

function updateDeferredValue(value){
const [prevValue, setValue] = mount(value);
updateEffect(() => {
const prevTransition = ReactCurrentBatchConfig.transition;
ReactCurrentBatchConfig.transition = 1;
try {
setValue(value);
} finally {
ReactCurrentBatchConfig.transition = prevTransition;
}
}, [value]);
return prevValue;
}

五、函数组件 update 阶段 Hook


useTransition 通过调用 updateState 基于传入的状态维护一个新的状态, 在 useEffect 中, 依赖传入状态的变化, 更新新的状态。更新新的状态之前, 会更新全局配置 currentBatchConfig.transitiontrue, 表示当前任务为过度任务。更新新的状态过程中会触发更新任务, 此时获取的任务优先级时, 会判断 currentBatchConfig.transition, 如果为 true, 那么此时的更新任务优先级就是 过渡任务优先级。更新新的状态完毕后, 将全局配置 currentBatchConfig.transition 恢复到之前状态。

function updateDeferredValue(value){
const [prevValue, setValue] = updateState(value);
updateEffect(() => {
const prevTransition = ReactCurrentBatchConfig.transition;
ReactCurrentBatchConfig.transition = 1;
try {
setValue(value);
} finally {
ReactCurrentBatchConfig.transition = prevTransition;
}
}, [value]);
return prevValue;
}