function bailoutOnLowPriority(current, workInProgress) { if (__DEV__) { cancelWorkTimer(workInProgress); } // TODO: Handle HostComponent tags here as well and call pushHostContext()? // See PR 8590 discussion for context switch (workInProgress.tag) { case HostRoot: pushHostRootContext(workInProgress); break; case ClassComponent: pushContextProvider(workInProgress); break; case HostPortal: pushHostContainer( workInProgress, workInProgress.stateNode.containerInfo, ); break; } // TODO: What if this is currently in progress? // How can that happen? How is this not being cloned? return null; }
function updateClassComponent( current: Fiber | null, workInProgress: Fiber, priorityLevel: PriorityLevel, ) { // Push context providers early to prevent context stack mismatches. // During mounting we don't know the child context yet as the instance doesn't exist. // We will invalidate the child context in finishClassComponent() right after rendering. const hasContext = pushContextProvider(workInProgress); let shouldUpdate; if (current === null) { if (!workInProgress.stateNode) { // In the initial pass we might need to construct the instance. constructClassInstance(workInProgress, workInProgress.pendingProps); mountClassInstance(workInProgress, priorityLevel); shouldUpdate = true; } else { // In a resume, we'll already have an instance we can reuse. shouldUpdate = resumeMountClassInstance(workInProgress, priorityLevel); } } else { shouldUpdate = updateClassInstance( current, workInProgress, priorityLevel, ); } return finishClassComponent( current, workInProgress, shouldUpdate, hasContext, ); }
function updateClassComponent(current : ?Fiber, workInProgress : Fiber) { let shouldUpdate; if (!current) { if (!workInProgress.stateNode) { // In the initial pass we might need to construct the instance. constructClassInstance(workInProgress); mountClassInstance(workInProgress); shouldUpdate = true; } else { // In a resume, we'll already have an instance we can reuse. shouldUpdate = resumeMountClassInstance(workInProgress); } } else { shouldUpdate = updateClassInstance(current, workInProgress); } if (!shouldUpdate) { return bailoutOnAlreadyFinishedWork(current, workInProgress); } // Rerender const instance = workInProgress.stateNode; ReactCurrentOwner.current = workInProgress; const nextChildren = instance.render(); reconcileChildren(current, workInProgress, nextChildren); // Put context on the stack because we will work on children if (isContextProvider(workInProgress)) { pushContextProvider(workInProgress, true); } return workInProgress.child; }
function bailoutOnAlreadyFinishedWork(current, workInProgress : Fiber) : ?Fiber { const priorityLevel = workInProgress.pendingWorkPriority; if (workInProgress.tag === HostComponent && workInProgress.memoizedProps.hidden && workInProgress.pendingWorkPriority !== OffscreenPriority) { // This subtree still has work, but it should be deprioritized so we need // to bail out and not do any work yet. // TODO: It would be better if this tree got its correct priority set // during scheduleUpdate instead because otherwise we'll start a higher // priority reconciliation first before we can get down here. However, // that is a bit tricky since workInProgress and current can have // different "hidden" settings. let child = workInProgress.progressedChild; while (child) { // To ensure that this subtree gets its priority reset, the children // need to be reset. child.pendingWorkPriority = OffscreenPriority; child = child.sibling; } return null; } // TODO: We should ideally be able to bail out early if the children have no // more work to do. However, since we don't have a separation of this // Fiber's priority and its children yet - we don't know without doing lots // of the same work we do anyway. Once we have that separation we can just // bail out here if the children has no more work at this priority level. // if (workInProgress.priorityOfChildren <= priorityLevel) { // // If there are side-effects in these children that have not yet been // // committed we need to ensure that they get properly transferred up. // if (current && current.child !== workInProgress.child) { // reuseChildrenEffects(workInProgress, child); // } // return null; // } if (current && workInProgress.child === current.child) { // If we had any progressed work already, that is invalid at this point so // let's throw it out. clearDeletions(workInProgress); } cloneChildFibers(current, workInProgress); markChildAsProgressed(current, workInProgress, priorityLevel); // Put context on the stack because we will work on children if (isContextProvider(workInProgress)) { pushContextProvider(workInProgress, false); } return workInProgress.child; }
function beginFailedWork( current: Fiber | null, workInProgress: Fiber, priorityLevel: PriorityLevel, ) { // Push context providers here to avoid a push/pop context mismatch. switch (workInProgress.tag) { case ClassComponent: pushContextProvider(workInProgress); break; case HostRoot: pushHostRootContext(workInProgress); break; default: invariant( false, 'Invalid type of work. This error is likely caused by a bug in React. ' + 'Please file an issue.', ); } // Add an error effect so we can handle the error during the commit phase workInProgress.effectTag |= Err; // This is a weird case where we do "resume" work — work that failed on // our first attempt. Because we no longer have a notion of "progressed // deletions," reset the child to the current child to make sure we delete // it again. TODO: Find a better way to handle this, perhaps during a more // general overhaul of error handling. if (current === null) { workInProgress.child = null; } else if (workInProgress.child !== current.child) { workInProgress.child = current.child; } if ( workInProgress.pendingWorkPriority === NoWork || workInProgress.pendingWorkPriority > priorityLevel ) { return bailoutOnLowPriority(current, workInProgress); } // If we don't bail out, we're going be recomputing our children so we need // to drop our effect list. workInProgress.firstEffect = null; workInProgress.lastEffect = null; // Unmount the current children as if the component rendered null const nextChildren = null; reconcileChildrenAtPriority( current, workInProgress, nextChildren, priorityLevel, ); if (workInProgress.tag === ClassComponent) { const instance = workInProgress.stateNode; workInProgress.memoizedProps = instance.props; workInProgress.memoizedState = instance.state; } return workInProgress.child; }
function mountIndeterminateComponent(current, workInProgress, priorityLevel) { invariant( current === null, 'An indeterminate component should never have mounted. This error is ' + 'likely caused by a bug in React. Please file an issue.', ); var fn = workInProgress.type; var props = workInProgress.pendingProps; var unmaskedContext = getUnmaskedContext(workInProgress); var context = getMaskedContext(workInProgress, unmaskedContext); var value; if (__DEV__) { ReactCurrentOwner.current = workInProgress; value = fn(props, context); } else { value = fn(props, context); } // React DevTools reads this flag. workInProgress.effectTag |= PerformedWork; if ( typeof value === 'object' && value !== null && typeof value.render === 'function' ) { // Proceed under the assumption that this is a class instance workInProgress.tag = ClassComponent; // Push context providers early to prevent context stack mismatches. // During mounting we don't know the child context yet as the instance doesn't exist. // We will invalidate the child context in finishClassComponent() right after rendering. const hasContext = pushContextProvider(workInProgress); adoptClassInstance(workInProgress, value); mountClassInstance(workInProgress, priorityLevel); return finishClassComponent(current, workInProgress, true, hasContext); } else { // Proceed under the assumption that this is a functional component workInProgress.tag = FunctionalComponent; if (__DEV__) { const Component = workInProgress.type; if (Component) { warning( !Component.childContextTypes, '%s(...): childContextTypes cannot be defined on a functional component.', Component.displayName || Component.name || 'Component', ); } if (workInProgress.ref !== null) { let info = ''; const ownerName = ReactDebugCurrentFiber.getCurrentFiberOwnerName(); if (ownerName) { info += '\n\nCheck the render method of `' + ownerName + '`.'; } let warningKey = ownerName || workInProgress._debugID || ''; const debugSource = workInProgress._debugSource; if (debugSource) { warningKey = debugSource.fileName + ':' + debugSource.lineNumber; } if (!warnedAboutStatelessRefs[warningKey]) { warnedAboutStatelessRefs[warningKey] = true; warning( false, 'Stateless function components cannot be given refs. ' + 'Attempts to access this ref will fail.%s%s', info, ReactDebugCurrentFiber.getCurrentFiberStackAddendum(), ); } } } reconcileChildren(current, workInProgress, value); memoizeProps(workInProgress, props); return workInProgress.child; } }