export function updateContainer(
element: ReactNodeList,
container: OpaqueRoot,
parentComponent: ?React$Component<any, any>,
callback: ?Function,
): ExpirationTime {
// createFiberRoot中创建的fiber对象
const current = container.current;
const currentTime = requestCurrentTime();
if (__DEV__) {
// $FlowExpectedError - jest isn't a global, and isn't recognized outside of tests
if ('undefined' !== typeof jest) {
warnIfNotScopedWithMatchingAct(current);
}
}
const suspenseConfig = requestCurrentSuspenseConfig();
// 获取任务到期时间
const expirationTime = computeExpirationForFiber(
currentTime,
current,
suspenseConfig,
);
return updateContainerAtExpirationTime(
element,
container,
parentComponent,
expirationTime,
suspenseConfig,
callback,
);
}
computeExpirationForFiber
export function computeExpirationForFiber(
currentTime: ExpirationTime,
fiber: Fiber,
suspenseConfig: null | SuspenseConfig,
): ExpirationTime {
const mode = fiber.mode;
if ((mode & BatchedMode) === NoMode) {
return Sync;
}
const priorityLevel = getCurrentPriorityLevel();
if ((mode & ConcurrentMode) === NoMode) {
return priorityLevel === ImmediatePriority ? Sync : Batched;
}
if ((executionContext & RenderContext) !== NoContext) {
// Use whatever time we're already rendering
return renderExpirationTime;
}
let expirationTime;
if (suspenseConfig !== null) {
// Compute an expiration time based on the Suspense timeout.
expirationTime = computeSuspenseExpiration(
currentTime,
suspenseConfig.timeoutMs | 0 || LOW_PRIORITY_EXPIRATION,
);
} else {
// Compute an expiration time based on the Scheduler priority.
switch (priorityLevel) {
case ImmediatePriority:
expirationTime = Sync;
break;
case UserBlockingPriority:
// TODO: Rename this to computeUserBlockingExpiration
expirationTime = computeInteractiveExpiration(currentTime);
break;
case NormalPriority:
case LowPriority: // TODO: Handle LowPriority
// TODO: Rename this to... something better.
expirationTime = computeAsyncExpiration(currentTime);
break;
case IdlePriority:
expirationTime = Never;
break;
default:
invariant(false, 'Expected a valid priority level');
}
}
// If we're in the middle of rendering a tree, do not update at the same
// expiration time that is already rendering.
// TODO: We shouldn't have to do this if the update is on a different root.
// Refactor computeExpirationForFiber + scheduleUpdate so we have access to
// the root when we check for this condition.
if (workInProgressRoot !== null && expirationTime === renderExpirationTime) {
// This is a trick to move this update into a separate batch
expirationTime -= 1;
}
return expirationTime;
}
updateContainerAtExpirationTime
// 根据渲染优先级更新dom
export function updateContainerAtExpirationTime(
element: ReactNodeList,
container: OpaqueRoot,
parentComponent: ?React$Component<any, any>,
expirationTime: ExpirationTime,
suspenseConfig: null | SuspenseConfig,
callback: ?Function,
) {
// TODO: If this is a nested container, this won't be the root.
const current = container.current;
if (__DEV__) {
if (ReactFiberInstrumentation.debugTool) {
if (current.alternate === null) {
ReactFiberInstrumentation.debugTool.onMountContainer(container);
} else if (element === null) {
ReactFiberInstrumentation.debugTool.onUnmountContainer(container);
} else {
ReactFiberInstrumentation.debugTool.onUpdateContainer(container);
}
}
}
// 获得上下文对象
const context = getContextForSubtree(parentComponent);
if (container.context === null) {
container.context = context;
} else {
container.pendingContext = context;
}
return scheduleRootUpdate(
current,
element,
expirationTime,
suspenseConfig,
callback,
);
}
getContextForSubtree
// 获得上下文对象
function getContextForSubtree(
parentComponent: ?React$Component<any, any>,
): Object {
if (!parentComponent) {
return emptyContextObject;
}
const fiber = getInstance(parentComponent);
const parentContext = findCurrentUnmaskedContext(fiber);
if (fiber.tag === ClassComponent) {
const Component = fiber.type;
if (isLegacyContextProvider(Component)) {
return processChildContext(fiber, Component, parentContext);
}
}
return parentContext;
}
updateContainer的源码很简单,通过computeExpirationForFiber获得计算优先级,然后丢给updateContainerAtExpirationTime,这里updateContainerAtExpirationTime其实相当于什么都没做,通过getContextForSubtree(这里getContextForSubtree因为一开始parentComponent是不存在的,于是返回一个空对象。注意,这个空对象可以重复使用,不用每次返回一个新的空对象,这是一个很好的优化)获得上下文对象,然后分配给container.context或container.pendingContext,最后一起丢给scheduleRootUpdate。