// Scheduler periodically yields in case there is other work on the main // thread, like user events. By default, it yields multiple times per frame. // It does not attempt to align with frame boundaries, since most tasks don't // need to be frame aligned; for those that do, use requestAnimationFrame. let yieldInterval = 5; const maxYieldInterval = 300; let needsPaint = false;
functionreconcileChildrenArray ( returnFiber: Fiber, currentFirstChild: Fiber | null, newChildren: Array<*>, expirationTime: ExpirationTime, ): Fiber | null { // This algorithm can't optimize by searching from both ends since we // don't have backpointers on fibers. I'm trying to see how far we can get // with that model. If it ends up not being worth the tradeoffs, we can // add it later.
// Even with a two ended optimization, we'd want to optimize for the case // where there are few changes and brute force the comparison instead of // going for the Map. It'd like to explore hitting that path first in // forward-only mode and only go for the Map once we notice that we need // lots of look ahead. This doesn't handle reversal as well as two ended // search but that's unusual. Besides, for the two ended optimization to // work on Iterables, we'd need to copy the whole set.
// In this first iteration, we'll just live with hitting the bad case // (adding everything to a Map) in for every insert/move.
// If you change this code, also update reconcileChildrenIterator() which // uses the same algorithm.
if (__DEV__) { // First, validate keys. let knownKeys = null; for (let i = 0; i < newChildren.length; i++) { const child = newChildren[i]; knownKeys = warnOnInvalidKey(child, knownKeys); } }
let oldFiber = currentFirstChild; let lastPlacedIndex = 0; let newIdx = 0; let nextOldFiber = null; for (; oldFiber !== null && newIdx < newChildren.length; newIdx++) { if (oldFiber.index > newIdx) { nextOldFiber = oldFiber; oldFiber = null; } else { nextOldFiber = oldFiber.sibling; } const newFiber = updateSlot( returnFiber, oldFiber, newChildren[newIdx], expirationTime, ); if (newFiber === null) { // TODO: This breaks on empty slots like null children. That's // unfortunate because it triggers the slow path all the time. We need // a better way to communicate whether this was a miss or null, // boolean, undefined, etc. if (oldFiber === null) { oldFiber = nextOldFiber; } break; } if (shouldTrackSideEffects) { if (oldFiber && newFiber.alternate === null) { // We matched the slot, but we didn't reuse the existing fiber, so we // need to delete the existing child. deleteChild(returnFiber, oldFiber); } } lastPlacedIndex = placeChild(newFiber, lastPlacedIndex, newIdx); if (previousNewFiber === null) { // TODO: Move out of the loop. This only happens for the first run. resultingFirstChild = newFiber; } else { // TODO: Defer siblings if we're not at the right index for this slot. // I.e. if we had null values before, then we want to defer this // for each null value. However, we also don't want to call updateSlot // with the previous one. previousNewFiber.sibling = newFiber; } previousNewFiber = newFiber; oldFiber = nextOldFiber; }
if (newIdx === newChildren.length) { // We've reached the end of the new children. We can delete the rest. deleteRemainingChildren(returnFiber, oldFiber); return resultingFirstChild; }
if (oldFiber === null) { // If we don't have any more existing children we can choose a fast path // since the rest will all be insertions. for (; newIdx < newChildren.length; newIdx++) {
// 源码中的处理 // Check if work was scheduled by one of the effects const rootExpirationTime = root.expirationTime; if (rootExpirationTime !== NoWork) { requestWork(root, rootExpirationTime); } // Flush any sync work that was scheduled by effects if (!isBatchingUpdates && !isRendering) { performSyncWork(); }
invariant( root.current !== finishedWork, 'Cannot commit the same tree as before. This is probably a bug ' + 'related to the return field. This error is likely caused by a bug ' + 'in React. Please file an issue.', ); const committedExpirationTime = root.pendingCommitExpirationTime; invariant( committedExpirationTime !== NoWork, 'Cannot commit an incomplete root. This error is likely caused by a ' + 'bug in React. Please file an issue.', ); root.pendingCommitExpirationTime = NoWork;
// Update the pending priority levels to account for the work that we are // about to commit. This needs to happen before calling the lifecycles, since // they may schedule additional updates. const updateExpirationTimeBeforeCommit = finishedWork.expirationTime; const childExpirationTimeBeforeCommit = finishedWork.childExpirationTime; const earliestRemainingTimeBeforeCommit = childExpirationTimeBeforeCommit > updateExpirationTimeBeforeCommit ? childExpirationTimeBeforeCommit : updateExpirationTimeBeforeCommit; markCommittedPriorityLevels(root, earliestRemainingTimeBeforeCommit);
letprevInteractions: Set<Interaction> = (null: any); if (enableSchedulerTracing) { // Restore any pending interactions at this point, // So that cascading work triggered during the render phase will be accounted for. prevInteractions = __interactionsRef.current; __interactionsRef.current = root.memoizedInteractions; }
// Reset this to null before calling lifecycles ReactCurrentOwner.current = null;
let firstEffect; if (finishedWork.effectTag > PerformedWork) { // A fiber's effect list consists only of its children, not itself. So if // the root has an effect, we need to add it to the end of the list. The // resulting list is the set that would belong to the root's parent, if // it had one; that is, all the effects in the tree including the root. if (finishedWork.lastEffect !== null) { finishedWork.lastEffect.nextEffect = finishedWork; firstEffect = finishedWork.firstEffect; } else { firstEffect = finishedWork;