referring to react core library, at packages/react-reconciler/src/ReactFiberBeginWork.js from line 2486 (line numbers will change. current file refers to this commit)
code is structured like this:
2491-25092511-26912696//... other code
function beginWork(
current: Fiber | null,
workInProgress: Fiber,
renderExpirationTime: ExpirationTime
): Fiber | null {
const updateExpirationTime = workInProgress.expirationTime;
if (__DEV__) {
if (workInProgress._debugNeedsRemount && current !== null) {
// This will restart the begin phase with a new fiber.
return remountFiber(
current,
workInProgress,
createFiberFromTypeAndProps(
workInProgress.type,
workInProgress.key,
workInProgress.pendingProps,
workInProgress._debugOwner || null,
workInProgress.mode,
workInProgress.expirationTime
)
);
}
}
if (current !== null) {
const oldProps = current.memoizedProps;
const newProps = workInProgress.pendingProps;
if (
oldProps !== newProps ||
hasLegacyContextChanged() ||
// Force a re-render if the implementation changed due to hot reload:
(__DEV__ ? workInProgress.type !== current.type : false)
) {
// If props or context changed, mark the fiber as having performed work.
// This may be unset if the props are determined to be equal later (memo).
didReceiveUpdate = true;
} else if (updateExpirationTime < renderExpirationTime) {
didReceiveUpdate = false;
// This fiber does not have any pending work. Bailout without entering
// the begin phase. There's still some bookkeeping we that needs to be done
// in this optimized path, mostly pushing stuff onto the stack.
switch (workInProgress.tag) {
case HostRoot:
pushHostRootContext(workInProgress);
resetHydrationState();
break;
case HostComponent:
pushHostContext(workInProgress);
if (
workInProgress.mode & ConcurrentMode &&
renderExpirationTime !== Never &&
shouldDeprioritizeSubtree(workInProgress.type, newProps)
) {
if (enableSchedulerTracing) {
markSpawnedWork(Never);
}
// Schedule this fiber to re-render at offscreen priority. Then bailout.
workInProgress.expirationTime = workInProgress.childExpirationTime = Never;
return null;
}
break;
case ClassComponent: {
const Component = workInProgress.type;
if (isLegacyContextProvider(Component)) {
pushLegacyContextProvider(workInProgress);
}
break;
}
case HostPortal:
pushHostContainer(workInProgress, workInProgress.stateNode.containerInfo);
break;
case ContextProvider: {
const newValue = workInProgress.memoizedProps.value;
pushProvider(workInProgress, newValue);
break;
}
case Profiler:
if (enableProfilerTimer) {
workInProgress.effectTag |= Update;
}
break;
case SuspenseComponent: {
const state: SuspenseState | null = workInProgress.memoizedState;
const didTimeout = state !== null;
if (didTimeout) {
// If this boundary is currently timed out, we need to decide
// whether to retry the primary children, or to skip over it and
// go straight to the fallback. Check the priority of the primary
// child fragment.
const primaryChildFragment: Fiber = (workInProgress.child: any);
const primaryChildExpirationTime = primaryChildFragment.childExpirationTime;
if (
primaryChildExpirationTime !== NoWork &&
primaryChildExpirationTime >= renderExpirationTime
) {
// The primary children have pending work. Use the normal path
// to attempt to render the primary children again.
return updateSuspenseComponent(current, workInProgress, renderExpirationTime);
} else {
pushSuspenseContext(
workInProgress,
setDefaultShallowSuspenseContext(suspenseStackCursor.current)
);
// The primary children do not have pending work with sufficient
// priority. Bailout.
const child = bailoutOnAlreadyFinishedWork(
current,
workInProgress,
renderExpirationTime
);
if (child !== null) {
// The fallback children have pending work. Skip over the
// primary children and work on the fallback.
return child.sibling;
} else {
return null;
}
}
} else {
pushSuspenseContext(
workInProgress,
setDefaultShallowSuspenseContext(suspenseStackCursor.current)
);
}
break;
}
case DehydratedSuspenseComponent: {
if (enableSuspenseServerRenderer) {
pushSuspenseContext(
workInProgress,
setDefaultShallowSuspenseContext(suspenseStackCursor.current)
);
// We know that this component will suspend again because if it has
// been unsuspended it has committed as a regular Suspense component.
// If it needs to be retried, it should have work scheduled on it.
workInProgress.effectTag |= DidCapture;
}
break;
}
case SuspenseListComponent: {
const didSuspendBefore = (current.effectTag & DidCapture) !== NoEffect;
const childExpirationTime = workInProgress.childExpirationTime;
if (childExpirationTime < renderExpirationTime) {
// If none of the children had any work, that means that none of
// them got retried so they'll still be blocked in the same way
// as before. We can fast bail out.
pushSuspenseContext(workInProgress, suspenseStackCursor.current);
if (didSuspendBefore) {
workInProgress.effectTag |= DidCapture;
}
return null;
}
if (didSuspendBefore) {
// If something was in fallback state last time, and we have all the
// same children then we're still in progressive loading state.
// Something might get unblocked by state updates or retries in the
// tree which will affect the tail. So we need to use the normal
// path to compute the correct tail.
return updateSuspenseListComponent(current, workInProgress, renderExpirationTime);
}
// If nothing suspended before and we're rendering the same children,
// then the tail doesn't matter. Anything new that suspends will work
// in the "together" mode, so we can continue from the state we had.
let renderState = workInProgress.memoizedState;
if (renderState !== null) {
// Reset to the "together" mode in case we've started a different
// update in the past but didn't complete it.
renderState.rendering = null;
renderState.tail = null;
}
pushSuspenseContext(workInProgress, suspenseStackCursor.current);
break;
}
case EventComponent:
if (enableFlareAPI) {
pushHostContextForEventComponent(workInProgress);
}
break;
}
return bailoutOnAlreadyFinishedWork(current, workInProgress, renderExpirationTime);
}
} else {
didReceiveUpdate = false;
}
// Before entering the begin phase, clear the expiration time.
workInProgress.expirationTime = NoWork;
switch (workInProgress.tag) {
case IndeterminateComponent: {
return mountIndeterminateComponent(
current,
workInProgress,
workInProgress.type,
renderExpirationTime
);
}
case LazyComponent: {
const elementType = workInProgress.elementType;
return mountLazyComponent(
current,
workInProgress,
elementType,
updateExpirationTime,
renderExpirationTime
);
}
case FunctionComponent: {
const Component = workInProgress.type;
const unresolvedProps = workInProgress.pendingProps;
const resolvedProps =
workInProgress.elementType === Component
? unresolvedProps
: resolveDefaultProps(Component, unresolvedProps);
return updateFunctionComponent(
current,
workInProgress,
Component,
resolvedProps,
renderExpirationTime
);
}
case ClassComponent: {
const Component = workInProgress.type;
const unresolvedProps = workInProgress.pendingProps;
const resolvedProps =
workInProgress.elementType === Component
? unresolvedProps
: resolveDefaultProps(Component, unresolvedProps);
return updateClassComponent(
current,
workInProgress,
Component,
resolvedProps,
renderExpirationTime
);
}
case HostRoot:
return updateHostRoot(current, workInProgress, renderExpirationTime);
case HostComponent:
return updateHostComponent(current, workInProgress, renderExpirationTime);
case HostText:
return updateHostText(current, workInProgress);
case SuspenseComponent:
return updateSuspenseComponent(current, workInProgress, renderExpirationTime);
case HostPortal:
return updatePortalComponent(current, workInProgress, renderExpirationTime);
case ForwardRef: {
const type = workInProgress.type;
const unresolvedProps = workInProgress.pendingProps;
const resolvedProps =
workInProgress.elementType === type
? unresolvedProps
: resolveDefaultProps(type, unresolvedProps);
return updateForwardRef(current, workInProgress, type, resolvedProps, renderExpirationTime);
}
case Fragment:
return updateFragment(current, workInProgress, renderExpirationTime);
case Mode:
return updateMode(current, workInProgress, renderExpirationTime);
case Profiler:
return updateProfiler(current, workInProgress, renderExpirationTime);
case ContextProvider:
return updateContextProvider(current, workInProgress, renderExpirationTime);
case ContextConsumer:
return updateContextConsumer(current, workInProgress, renderExpirationTime);
case MemoComponent: {
const type = workInProgress.type;
const unresolvedProps = workInProgress.pendingProps;
// Resolve outer props first, then resolve inner props.
let resolvedProps = resolveDefaultProps(type, unresolvedProps);
if (__DEV__) {
if (workInProgress.type !== workInProgress.elementType) {
const outerPropTypes = type.propTypes;
if (outerPropTypes) {
checkPropTypes(
outerPropTypes,
resolvedProps, // Resolved for outer only
"prop",
getComponentName(type),
getCurrentFiberStackInDev
);
}
}
}
resolvedProps = resolveDefaultProps(type.type, resolvedProps);
return updateMemoComponent(
current,
workInProgress,
type,
resolvedProps,
updateExpirationTime,
renderExpirationTime
);
}
case SimpleMemoComponent: {
return updateSimpleMemoComponent(
current,
workInProgress,
workInProgress.type,
workInProgress.pendingProps,
updateExpirationTime,
renderExpirationTime
);
}
case IncompleteClassComponent: {
const Component = workInProgress.type;
const unresolvedProps = workInProgress.pendingProps;
const resolvedProps =
workInProgress.elementType === Component
? unresolvedProps
: resolveDefaultProps(Component, unresolvedProps);
return mountIncompleteClassComponent(
current,
workInProgress,
Component,
resolvedProps,
renderExpirationTime
);
}
case DehydratedSuspenseComponent: {
if (enableSuspenseServerRenderer) {
return updateDehydratedSuspenseComponent(current, workInProgress, renderExpirationTime);
}
break;
}
case SuspenseListComponent: {
return updateSuspenseListComponent(current, workInProgress, renderExpirationTime);
}
case EventComponent: {
if (enableFlareAPI) {
return updateEventComponent(current, workInProgress, renderExpirationTime);
}
break;
}
}
invariant(
false,
"Unknown unit of work tag. This error is likely caused by a bug in " +
"React. Please file an issue."
);
}
export { beginWork };