in Libraries/Renderer/implementations/ReactFabric-dev.fb.js [13500:14052]
function completeWork(current, workInProgress, renderLanes) {
var newProps = workInProgress.pendingProps;
switch (workInProgress.tag) {
case IndeterminateComponent:
case LazyComponent:
case SimpleMemoComponent:
case FunctionComponent:
case ForwardRef:
case Fragment:
case Mode:
case Profiler:
case ContextConsumer:
case MemoComponent:
bubbleProperties(workInProgress);
return null;
case ClassComponent: {
var Component = workInProgress.type;
if (isContextProvider(Component)) {
popContext(workInProgress);
}
bubbleProperties(workInProgress);
return null;
}
case HostRoot: {
var fiberRoot = workInProgress.stateNode;
popHostContainer(workInProgress);
popTopLevelContextObject(workInProgress);
resetWorkInProgressVersions();
if (fiberRoot.pendingContext) {
fiberRoot.context = fiberRoot.pendingContext;
fiberRoot.pendingContext = null;
}
if (current === null || current.child === null) {
// If we hydrated, pop so that we can delete any remaining children
// that weren't hydrated.
var wasHydrated = popHydrationState();
if (wasHydrated) {
// If we hydrated, then we'll need to schedule an update for
// the commit side-effects on the root.
markUpdate(workInProgress);
} else if (!fiberRoot.hydrate) {
// Schedule an effect to clear this container at the start of the next commit.
// This handles the case of React rendering into a container with previous children.
// It's also safe to do for updates too, because current.child would only be null
// if the previous render was null (so the the container would already be empty).
workInProgress.flags |= Snapshot;
}
}
updateHostContainer(current, workInProgress);
bubbleProperties(workInProgress);
return null;
}
case HostComponent: {
popHostContext(workInProgress);
var rootContainerInstance = getRootHostContainer();
var type = workInProgress.type;
if (current !== null && workInProgress.stateNode != null) {
updateHostComponent(
current,
workInProgress,
type,
newProps,
rootContainerInstance
);
if (current.ref !== workInProgress.ref) {
markRef(workInProgress);
}
} else {
if (!newProps) {
if (!(workInProgress.stateNode !== null)) {
throw Error(
"We must have new props for new mounts. This error is likely caused by a bug in React. Please file an issue."
);
} // This can happen when we abort work.
bubbleProperties(workInProgress);
return null;
}
var currentHostContext = getHostContext(); // TODO: Move createInstance to beginWork and keep it on a context
// "stack" as the parent. Then append children as we go in beginWork
// or completeWork depending on whether we want to add them top->down or
// bottom->up. Top->down is faster in IE11.
var _wasHydrated = popHydrationState();
if (_wasHydrated) {
// TODO: Move this and createInstance step into the beginPhase
// to consolidate.
if (prepareToHydrateHostInstance()) {
// If changes to the hydrated node need to be applied at the
// commit-phase we mark this as such.
markUpdate(workInProgress);
}
} else {
var instance = createInstance(
type,
newProps,
rootContainerInstance,
currentHostContext,
workInProgress
);
appendAllChildren(instance, workInProgress, false, false);
workInProgress.stateNode = instance; // Certain renderers require commit-time effects for initial mount.
}
if (workInProgress.ref !== null) {
// If there is a ref on a host node we need to schedule a callback
markRef(workInProgress);
}
}
bubbleProperties(workInProgress);
return null;
}
case HostText: {
var newText = newProps;
if (current && workInProgress.stateNode != null) {
var oldText = current.memoizedProps; // If we have an alternate, that means this is an update and we need
// to schedule a side-effect to do the updates.
updateHostText(current, workInProgress, oldText, newText);
} else {
if (typeof newText !== "string") {
if (!(workInProgress.stateNode !== null)) {
throw Error(
"We must have new props for new mounts. This error is likely caused by a bug in React. Please file an issue."
);
} // This can happen when we abort work.
}
var _rootContainerInstance = getRootHostContainer();
var _currentHostContext = getHostContext();
var _wasHydrated2 = popHydrationState();
if (_wasHydrated2) {
if (prepareToHydrateHostTextInstance()) {
markUpdate(workInProgress);
}
} else {
workInProgress.stateNode = createTextInstance(
newText,
_rootContainerInstance,
_currentHostContext,
workInProgress
);
}
}
bubbleProperties(workInProgress);
return null;
}
case SuspenseComponent: {
popSuspenseContext(workInProgress);
var nextState = workInProgress.memoizedState;
if ((workInProgress.flags & DidCapture) !== NoFlags) {
// Something suspended. Re-render with the fallback children.
workInProgress.lanes = renderLanes; // Do not reset the effect list.
if ((workInProgress.mode & ProfileMode) !== NoMode) {
transferActualDuration(workInProgress);
} // Don't bubble properties in this case.
return workInProgress;
}
var nextDidTimeout = nextState !== null;
var prevDidTimeout = false;
if (current === null);
else {
var prevState = current.memoizedState;
prevDidTimeout = prevState !== null;
} // If the suspended state of the boundary changes, we need to schedule
// an effect to toggle the subtree's visibility. When we switch from
// fallback -> primary, the inner Offscreen fiber schedules this effect
// as part of its normal complete phase. But when we switch from
// primary -> fallback, the inner Offscreen fiber does not have a complete
// phase. So we need to schedule its effect here.
//
// We also use this flag to connect/disconnect the effects, but the same
// logic applies: when re-connecting, the Offscreen fiber's complete
// phase will handle scheduling the effect. It's only when the fallback
// is active that we have to do anything special.
if (nextDidTimeout && !prevDidTimeout) {
var offscreenFiber = workInProgress.child;
offscreenFiber.flags |= Visibility; // TODO: This will still suspend a synchronous tree if anything
// in the concurrent tree already suspended during this render.
// This is a known bug.
if ((workInProgress.mode & ConcurrentMode) !== NoMode) {
// TODO: Move this back to throwException because this is too late
// if this is a large tree which is common for initial loads. We
// don't know if we should restart a render or not until we get
// this marker, and this is too late.
// If this render already had a ping or lower pri updates,
// and this is the first time we know we're going to suspend we
// should be able to immediately restart from within throwException.
var hasInvisibleChildContext =
current === null &&
workInProgress.memoizedProps.unstable_avoidThisFallback !== true;
if (
hasInvisibleChildContext ||
hasSuspenseContext(
suspenseStackCursor.current,
InvisibleParentSuspenseContext
)
) {
// If this was in an invisible tree or a new render, then showing
// this boundary is ok.
renderDidSuspend();
} else {
// Otherwise, we're going to have to hide content so we should
// suspend for longer if possible.
renderDidSuspendDelayIfPossible();
}
}
}
var wakeables = workInProgress.updateQueue;
if (wakeables !== null) {
// Schedule an effect to attach a retry listener to the promise.
// TODO: Move to passive phase
workInProgress.flags |= Update;
}
bubbleProperties(workInProgress);
{
if ((workInProgress.mode & ProfileMode) !== NoMode) {
if (nextDidTimeout) {
// Don't count time spent in a timed out Suspense subtree as part of the base duration.
var _primaryChildFragment2 = workInProgress.child;
if (_primaryChildFragment2 !== null) {
// $FlowFixMe Flow doesn't support type casting in combination with the -= operator
workInProgress.treeBaseDuration -=
_primaryChildFragment2.treeBaseDuration;
}
}
}
}
return null;
}
case HostPortal:
popHostContainer(workInProgress);
updateHostContainer(current, workInProgress);
if (current === null) {
preparePortalMount(workInProgress.stateNode.containerInfo);
}
bubbleProperties(workInProgress);
return null;
case ContextProvider:
// Pop provider fiber
var context = workInProgress.type._context;
popProvider(context, workInProgress);
bubbleProperties(workInProgress);
return null;
case IncompleteClassComponent: {
// Same as class component case. I put it down here so that the tags are
// sequential to ensure this switch is compiled to a jump table.
var _Component = workInProgress.type;
if (isContextProvider(_Component)) {
popContext(workInProgress);
}
bubbleProperties(workInProgress);
return null;
}
case SuspenseListComponent: {
popSuspenseContext(workInProgress);
var renderState = workInProgress.memoizedState;
if (renderState === null) {
// We're running in the default, "independent" mode.
// We don't do anything in this mode.
bubbleProperties(workInProgress);
return null;
}
var didSuspendAlready = (workInProgress.flags & DidCapture) !== NoFlags;
var renderedTail = renderState.rendering;
if (renderedTail === null) {
// We just rendered the head.
if (!didSuspendAlready) {
// This is the first pass. We need to figure out if anything is still
// suspended in the rendered set.
// If new content unsuspended, but there's still some content that
// didn't. Then we need to do a second pass that forces everything
// to keep showing their fallbacks.
// We might be suspended if something in this render pass suspended, or
// something in the previous committed pass suspended. Otherwise,
// there's no chance so we can skip the expensive call to
// findFirstSuspended.
var cannotBeSuspended =
renderHasNotSuspendedYet() &&
(current === null || (current.flags & DidCapture) === NoFlags);
if (!cannotBeSuspended) {
var row = workInProgress.child;
while (row !== null) {
var suspended = findFirstSuspended(row);
if (suspended !== null) {
didSuspendAlready = true;
workInProgress.flags |= DidCapture;
cutOffTailIfNeeded(renderState, false); // If this is a newly suspended tree, it might not get committed as
// part of the second pass. In that case nothing will subscribe to
// its thenables. Instead, we'll transfer its thenables to the
// SuspenseList so that it can retry if they resolve.
// There might be multiple of these in the list but since we're
// going to wait for all of them anyway, it doesn't really matter
// which ones gets to ping. In theory we could get clever and keep
// track of how many dependencies remain but it gets tricky because
// in the meantime, we can add/remove/change items and dependencies.
// We might bail out of the loop before finding any but that
// doesn't matter since that means that the other boundaries that
// we did find already has their listeners attached.
var newThenables = suspended.updateQueue;
if (newThenables !== null) {
workInProgress.updateQueue = newThenables;
workInProgress.flags |= Update;
} // Rerender the whole list, but this time, we'll force fallbacks
// to stay in place.
// Reset the effect flags before doing the second pass since that's now invalid.
// Reset the child fibers to their original state.
workInProgress.subtreeFlags = NoFlags;
resetChildFibers(workInProgress, renderLanes); // Set up the Suspense Context to force suspense and immediately
// rerender the children.
pushSuspenseContext(
workInProgress,
setShallowSuspenseContext(
suspenseStackCursor.current,
ForceSuspenseFallback
)
); // Don't bubble properties in this case.
return workInProgress.child;
}
row = row.sibling;
}
}
if (renderState.tail !== null && now() > getRenderTargetTime()) {
// We have already passed our CPU deadline but we still have rows
// left in the tail. We'll just give up further attempts to render
// the main content and only render fallbacks.
workInProgress.flags |= DidCapture;
didSuspendAlready = true;
cutOffTailIfNeeded(renderState, false); // Since nothing actually suspended, there will nothing to ping this
// to get it started back up to attempt the next item. While in terms
// of priority this work has the same priority as this current render,
// it's not part of the same transition once the transition has
// committed. If it's sync, we still want to yield so that it can be
// painted. Conceptually, this is really the same as pinging.
// We can use any RetryLane even if it's the one currently rendering
// since we're leaving it behind on this node.
workInProgress.lanes = SomeRetryLane;
}
} else {
cutOffTailIfNeeded(renderState, false);
} // Next we're going to render the tail.
} else {
// Append the rendered row to the child list.
if (!didSuspendAlready) {
var _suspended = findFirstSuspended(renderedTail);
if (_suspended !== null) {
workInProgress.flags |= DidCapture;
didSuspendAlready = true; // Ensure we transfer the update queue to the parent so that it doesn't
// get lost if this row ends up dropped during a second pass.
var _newThenables = _suspended.updateQueue;
if (_newThenables !== null) {
workInProgress.updateQueue = _newThenables;
workInProgress.flags |= Update;
}
cutOffTailIfNeeded(renderState, true); // This might have been modified.
if (
renderState.tail === null &&
renderState.tailMode === "hidden" &&
!renderedTail.alternate &&
!getIsHydrating() // We don't cut it if we're hydrating.
) {
// We're done.
bubbleProperties(workInProgress);
return null;
}
} else if (
// The time it took to render last row is greater than the remaining
// time we have to render. So rendering one more row would likely
// exceed it.
now() * 2 - renderState.renderingStartTime >
getRenderTargetTime() &&
renderLanes !== OffscreenLane
) {
// We have now passed our CPU deadline and we'll just give up further
// attempts to render the main content and only render fallbacks.
// The assumption is that this is usually faster.
workInProgress.flags |= DidCapture;
didSuspendAlready = true;
cutOffTailIfNeeded(renderState, false); // Since nothing actually suspended, there will nothing to ping this
// to get it started back up to attempt the next item. While in terms
// of priority this work has the same priority as this current render,
// it's not part of the same transition once the transition has
// committed. If it's sync, we still want to yield so that it can be
// painted. Conceptually, this is really the same as pinging.
// We can use any RetryLane even if it's the one currently rendering
// since we're leaving it behind on this node.
workInProgress.lanes = SomeRetryLane;
}
}
if (renderState.isBackwards) {
// The effect list of the backwards tail will have been added
// to the end. This breaks the guarantee that life-cycles fire in
// sibling order but that isn't a strong guarantee promised by React.
// Especially since these might also just pop in during future commits.
// Append to the beginning of the list.
renderedTail.sibling = workInProgress.child;
workInProgress.child = renderedTail;
} else {
var previousSibling = renderState.last;
if (previousSibling !== null) {
previousSibling.sibling = renderedTail;
} else {
workInProgress.child = renderedTail;
}
renderState.last = renderedTail;
}
}
if (renderState.tail !== null) {
// We still have tail rows to render.
// Pop a row.
var next = renderState.tail;
renderState.rendering = next;
renderState.tail = next.sibling;
renderState.renderingStartTime = now();
next.sibling = null; // Restore the context.
// TODO: We can probably just avoid popping it instead and only
// setting it the first time we go from not suspended to suspended.
var suspenseContext = suspenseStackCursor.current;
if (didSuspendAlready) {
suspenseContext = setShallowSuspenseContext(
suspenseContext,
ForceSuspenseFallback
);
} else {
suspenseContext = setDefaultShallowSuspenseContext(suspenseContext);
}
pushSuspenseContext(workInProgress, suspenseContext); // Do a pass over the next row.
// Don't bubble properties in this case.
return next;
}
bubbleProperties(workInProgress);
return null;
}
case ScopeComponent: {
break;
}
case OffscreenComponent:
case LegacyHiddenComponent: {
popRenderLanes(workInProgress);
var _nextState = workInProgress.memoizedState;
var nextIsHidden = _nextState !== null;
if (current !== null) {
var _prevState = current.memoizedState;
var prevIsHidden = _prevState !== null;
if (
prevIsHidden !== nextIsHidden &&
newProps.mode !== "unstable-defer-without-hiding" && // LegacyHidden doesn't do any hiding — it only pre-renders.
workInProgress.tag !== LegacyHiddenComponent
) {
workInProgress.flags |= Visibility;
}
}
if (!nextIsHidden || (workInProgress.mode & ConcurrentMode) === NoMode) {
bubbleProperties(workInProgress);
} else {
// Don't bubble properties for hidden children unless we're rendering
// at offscreen priority.
if (includesSomeLane(subtreeRenderLanes, OffscreenLane)) {
bubbleProperties(workInProgress);
}
}
return null;
}
}
{
throw Error(
"Unknown unit of work tag (" +
workInProgress.tag +
"). This error is likely caused by a bug in React. Please file an issue."
);
}
}