Update & UpdateQueue
Cosen95 opened this issue · 0 comments
Cosen95 commented
Update & UpdateQueue
创建了RootFiber
对象和FiberRoot
对象之后,接下来就是处理更新。对应updateContainer
:
// packages/react-reconciler/src/ReactFiberReconciler.new.js
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();
if (__DEV__) {
// $FlowExpectedError - jest isn't a global, and isn't recognized outside of tests
if ("undefined" !== typeof jest) {
warnIfUnmockedScheduler(current);
warnIfNotScopedWithMatchingAct(current);
}
}
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.",
getComponentName(ReactCurrentFiberCurrent.type) || "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);
scheduleUpdateOnFiber(current, lane, eventTime);
return lane;
}
可以看到通过createUpdate()
函数创建出一个update
对象。
首先需要明白Update
对象到底是什么?它主要用于记录组件状态的改变,存放在UpdateQueue
中,通过计算得到一个最终的组件更新状态。同时多个Update
状态是可以同时存在的。
下面是createUpdate
函数所做的事情,其实就是包装了一层转换成了其他数据结构返回。
export function createUpdate(eventTime: number, lane: Lane): Update<*> {
const update: Update<*> = {
eventTime,
lane,
tag: UpdateState,
payload: null,
callback: null,
next: null,
};
return update;
}
其中tag
属性对应四种情况,根据不同的情况执行不同的动作:
// packages/react-reconciler/src/ReactUpdateQueue.new.js
export const UpdateState = 0;
export const ReplaceState = 1;
export const ForceUpdate = 2;
export const CaptureUpdate = 3; // 捕获渲染错误时会生成一个Update
这里顺便来看下Update
对象:
export type Update<State> = {|
// TODO: Temporary field. Will remove this by storing a map of
// transition -> event time on the root.
// 更新的过期时间
eventTime: number,
lane: Lane,
// export const UpdateState = 0;
// export const ReplaceState = 1;
// export const ForceUpdate = 2;
// export const CaptureUpdate = 3;
// 指定更新的类型,值为以上几种
tag: 0 | 1 | 2 | 3,
// 更新内容,比如`setState`接收的第一个参数
payload: any,
// 对应的回调,`setState`,`render`都有
callback: (() => mixed) | null,
// 指向下一个更新
next: Update<State> | null,
|};
创建了Update
对象后,将Update
对象传入enqueueUpdate()
,下面是enqueueUpdate()
所做的事情:
export function enqueueUpdate<State>(fiber: Fiber, update: Update<State>) {
// 存储执行中的更新任务 Update 队列,尾节点存储形式
const updateQueue = fiber.updateQueue;
if (updateQueue === null) {
// Only occurs if the fiber has been unmounted.
return;
}
//以 pending 属性存储待执行的更新任务 Update 队列,尾节点存储形式
const sharedQueue: SharedQueue<State> = (updateQueue: any).shared;
const pending = sharedQueue.pending;
if (pending === null) {
// This is the first update. Create a circular list.
// 第一次更新,创建一个循环列表
update.next = update;
} else {
update.next = pending.next;
pending.next = update;
}
sharedQueue.pending = update;
if (__DEV__) {
if (
currentlyProcessingQueue === sharedQueue &&
!didWarnUpdateInsideUpdate
) {
console.error(
"An update (setState, replaceState, or forceUpdate) was scheduled " +
"from inside an update function. Update functions should be pure, " +
"with zero side-effects. Consider using componentDidUpdate or a " +
"callback."
);
didWarnUpdateInsideUpdate = true;
}
}
}
由上可以看出enqueueUpdate()
函数主要是进行创建和更新UpdateQueue
的操作。