认识
一、认识
useDeferredValue
用于在 createRoot
开启的并发特性模式下, 开启并发渲染。主要解决浏览器执行大量渲染工作问题时的卡顿问题。useDeferredValue
传入一个可能会引发大量渲染工作的状态, 返回一个新的状态。useDeferredValue
把传入的状态通过过渡任务得到新的值,这个值作为延时状态。
1.1 useTransition vs useDeferredValue
useDeferredValue
本质上和内部实现与 useTransition
一样都是标记成了过渡更新任务。但是, useTransition
是把 startTransition
内部的更新任务变成了过渡任务 transition
,而 useDeferredValue
是把原值通过过渡任务得到新的值,这个值作为延时状态。 一个是处理一段逻辑,另一个是生产一个新的状态。useDeferredValue
还有一个不同点就是这个任务,本质上在 useEffect
内部执行,而 useEffect
内部逻辑是异步执行的 ,所以它一定程度上更滞后于 useTransition
。 useDeferredValue = 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.js
的 beginWork
阶段, 遇到 FunctionComponent
类型的 Fiber
, 会执行 updateFunctionComponent
。updateFunctionComponent
内部调用 renderWithHooks
。主要工作如下:
-
重置状态: 函数组件执行前, 重置当前正在处理的
hook
、当前正在使用的hook
-
记录当前组件
fiber
: 通过currentlyRenderingFiber
变量记录当前组件fiber
-
选择对应阶段的
Hook
:mount
阶段选择HooksDispatcherOnMount
作为Hooks Map
,update
阶段选择HooksDispatcherOnUpdate
作为Hooks Map
。 函数组件mount
阶段 创建hook
数据结构, 存储到fiber.memoizedState
或者hook.next
, 初次建立其hooks
与fiber
之间的关系。函数组件update
阶段 找到当前函数组件fiber
, 找到当前hook
, 更新hook
状态。 -
执行函数组件: 执行我们真正函数组件,所有的
hooks
将依次执行, 每个hook
内部要根据currentlyRenderingFiber
读取对应的内容, -
重置状态: 执行完
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.transition
为 true
, 表示当前任务为过度任务。更新新的状态过程中会触发更新任务, 此时获取的任务优先级时, 会判断 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.transition
为 true
, 表示当前任务为过度任务。更新新的状态过程中会触发更新任务, 此时获取的任务优先级时, 会判断 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;
}