目录
2021SC@SDUSC
介绍getStateFromUpdate
在getStateFromUpdate函数中,通过更新任务将State进行更新。
function getStateFromUpdate<State>(
workInProgress: Fiber,
queue: UpdateQueue<State>,
update: Update<State>,
prevState: State,
nextProps: any,
instance: any,
): any {
switch (update.tag) {
case ReplaceState: {
const payload = update.payload;
if (typeof payload === 'function') {
// Updater function
const nextState = payload.call(instance, prevState, nextProps);
return nextState;
}
// State object
return payload;
}
case CaptureUpdate: {
workInProgress.flags =
(workInProgress.flags & ~ShouldCapture) | DidCapture;
}
// Intentional fallthrough
case UpdateState: {
const payload = update.payload;
let partialState;
if (typeof payload === 'function') {
// Updater function
partialState = payload.call(instance, prevState, nextProps);
} else {
// Partial state object
partialState = payload;
}
if (partialState === null || partialState === undefined) {
// Null and undefined are treated as no-ops.
return prevState;
}
// Merge the partial state and the previous state.
return Object.assign({}, prevState, partialState);
}
case ForceUpdate: {
hasForceUpdate = true;
return prevState;
}
}
return prevState;
}
首先辨认更新过程中的标志(也就是update.tag),假如是ReplaceState,也就是对State进行替换,调用update中的payload进行计算实例中的新State(如果payload为函数),假如payload并非函数那就直接将payload返回。当标志为UpdateState时,计算新State的方法与上面类似,但这里会将计算出的新属性与以前的State合并作为newState返回。假如是ForceUpdate,则将hasForceUpdate标记为true,以后再进行处理。假如是CaptureUpdate,那么就更改节点中的Flag,这里涉及到Flow的位运算技巧,假如应当捕获的话,就会将其中的ShouldCapture属性移除并添加DidCapture属性。
谈谈update任务在react中的调用处理
首先,update的创建以及放入updateQueue的位置主要是在updateContainer处,以下为源码:
export function updateContainer(
element: ReactNodeList,
container: OpaqueRoot,
parentComponent: ?React$Component<any, any>,
callback: ?Function,
): Lane {
if (__DEV__) {
onScheduleRoot(container, element);
}
const current = container.current;
const eventTime = requestEventTime();
const lane = requestUpdateLane(current);
if (enableSchedulingProfiler) {
markRenderScheduled(lane);
}
const context = getContextForSubtree(parentComponent);
if (container.context === null) {
container.context = context;
} else {
container.pendingContext = context;
}
if (__DEV__) {
if (
ReactCurrentFiberIsRendering &&
ReactCurrentFiberCurrent !== null &&
!didWarnAboutNestedUpdates
) {
didWarnAboutNestedUpdates = true;
console.error(
'Render methods should be a pure function of props and state; ' +
'triggering nested component updates from render is not allowed. ' +
'If necessary, trigger nested updates in componentDidUpdate.\n\n' +
'Check the render method of %s.',
getComponentNameFromFiber(ReactCurrentFiberCurrent) || 'Unknown',
);
}
}
const update = createUpdate(eventTime, lane);
// Caution: React DevTools currently depends on this property
// being called "element".
update.payload = {element};
callback = callback === undefined ? null : callback;
if (callback !== null) {
if (__DEV__) {
if (typeof callback !== 'function') {
console.error(
'render(...): Expected the last optional `callback` argument to be a ' +
'function. Instead received: %s.',
callback,
);
}
}
update.callback = callback;
}
enqueueUpdate(current, update, lane);
const root = scheduleUpdateOnFiber(current, lane, eventTime);
if (root !== null) {
entangleTransitions(root, current, lane);
}
return lane;
}
export {
batchedUpdates,
deferredUpdates,
discreteUpdates,
flushControlled,
flushSync,
flushSyncWithoutWarningIfAlreadyRendering,
flushPassiveEffects,
};
在update中,update中的eventTime根据requestEventTime获取,而lane则根据container中的current节点进行判断,然后根据eventTime与lane创建update,并将其标签设置为updateState,含义为更新状态,这在上一篇博客中已经阐述。将update的载荷设置为当前节点的element,然后将callback设置为update的callback,然后通过enqueueUpdate将update放入updateQueue中。至于对update的处理procressUpdateQueue,在多处都有所调用,主要集中在workLoop中beginWork函数的更新节点部分(比如updateClassComponent,updateHostRoot等),在beginWork中对节点的updateQueue进行调用,从而得到新的状态。
Lane模型简介
Lane同样为优先级机制,但与Scheduler不同,属于另一套优先级机制,有不同的lane种类代表不同的优先级,这里给出lane的种种类型。
// Lane values below should be kept in sync with getLabelForLane(), used by react-devtools-scheduling-profiler.
// If those values are changed that package should be rebuilt and redeployed.
export const TotalLanes = 31;
export const NoLanes: Lanes = /* */ 0b0000000000000000000000000000000;
export const NoLane: Lane = /* */ 0b0000000000000000000000000000000;
export const SyncLane: Lane = /* */ 0b0000000000000000000000000000001;
export const InputContinuousHydrationLane: Lane = /* */ 0b0000000000000000000000000000010;
export const InputContinuousLane: Lanes = /* */ 0b0000000000000000000000000000100;
export const DefaultHydrationLane: Lane = /* */ 0b0000000000000000000000000001000;
export const DefaultLane: Lanes = /* */ 0b0000000000000000000000000010000;
这里用1的个数来代表lane中的优先级,这其中,可将lane比喻为车道,1表明可在其中的车道中行驶,又根据范围划分为不同的车道区间,这里通过一些lane的计算方法体现lane的作用,首先是在处理更新队列过程中的函数isSubsetOfLanes,以下为源码
export function isSubsetOfLanes(set: Lanes, subset: Lanes | Lane) {
return (set & subset) === subset;
}
前面为lane区间,后者为lane区间或者lane,将第一个参数的lane区间与第二个参数进行与运算,由二进制的位运算很容易得知假如第二个参数为第一个参数的子区间或者元素,那么两者的与结果就是子区间或者元素本身。这也可以解释为何Nolane总是在任意lane区间中。
接下来是processUpdateQueue中的mergeLanes函数:
export function mergeLanes(a: Lanes | Lane, b: Lanes | Lane): Lanes {
return a | b;
}
通过或运算将两个Lane区间进行合并从而为节点增添另一个区间。
这就是对update中lane优先级的简单介绍。
谈谈react源码中的位运算
首先的用途,就像在上文中所描述的那样,方便Lane模型的运算。另外,还有一些在属性标记过程中常用的符号,比如:
workInProgress.flags |= PerformedWork;
含义为在workInProgress的flags中添加performedWork。
再比如上文中所提到的
workInProgress.flags =
(workInProgress.flags & ~ShouldCapture) | DidCapture;
含义为首先在workInProgress的flags中移除ShouldCapture并且添加DidCapture属性。
总结
在这里介绍了处理updateQueue中的getStateFromUpdate函数,updateQueue在react源码中的具体调用的位置和相关处理,Lane模型的具体原理以及react源码中的与位运算相关的应用。