@samitouri / QOS-React / commits / fd03a86429

Always reconcile against current children (#11564)

The new resuming algorithm will always reconcile against the current child set, even if there's a newer work-in-progress child set.

Andrew Clark committed Nov 16, 2017 at 16:58 UTC fd03a86429122c4e3cd4ec01db7552bd8391ad91
2 files changed +19 -61
packages/react-reconciler/src/ReactChildFiber.js
+8 -32
@@ -13,7 +13,7 @@ import type {Fiber} from 'react-reconciler/src/ReactFiber';
13 import type {ExpirationTime} from 'react-reconciler/src/ReactFiberExpirationTime';
14
15 import {enableReactFragment} from 'shared/ReactFeatureFlags';
16 -import {NoEffect, Placement, Deletion} from 'shared/ReactTypeOfSideEffect';
16 +import {Placement, Deletion} from 'shared/ReactTypeOfSideEffect';
17 import {
18 FunctionalComponent,
19 ClassComponent,
@@ -222,21 +222,12 @@ function warnOnFunctionType() {
222 // to be able to optimize each path individually by branching early. This needs
223 // a compiler or we can do it manually. Helpers that don't need this branching
224 // live outside of this function.
225 -function ChildReconciler(shouldClone, shouldTrackSideEffects) {
225 +function ChildReconciler(shouldTrackSideEffects) {
226 function deleteChild(returnFiber: Fiber, childToDelete: Fiber): void {
227 if (!shouldTrackSideEffects) {
228 // Noop.
229 return;
230 }
231 - if (!shouldClone) {
232 - // When we're reconciling in place we have a work in progress copy. We
233 - // actually want the current copy. If there is no current copy, then we
234 - // don't need to track deletion side-effects.
235 - if (childToDelete.alternate === null) {
236 - return;
237 - }
238 - childToDelete = childToDelete.alternate;
239 - }
231 // Deletions are added in reversed order so we add it to the front.
232 // At this point, the return fiber's effect list is empty except for
233 // deletions, so we can just append the deletion to the list. The remaining
@@ -300,22 +291,10 @@ function ChildReconciler(shouldClone, shouldTrackSideEffects) {
291 ): Fiber {
292 // We currently set sibling to null and index to 0 here because it is easy
293 // to forget to do before returning it. E.g. for the single child case.
303 - if (shouldClone) {
304 - const clone = createWorkInProgress(fiber, pendingProps, expirationTime);
305 - clone.index = 0;
306 - clone.sibling = null;
307 - return clone;
308 - } else {
309 - // We override the expiration time even if it is earlier, because if
310 - // we're reconciling at a later time that means that this was
311 - // down-prioritized.
312 - fiber.expirationTime = expirationTime;
313 - fiber.effectTag = NoEffect;
314 - fiber.index = 0;
315 - fiber.sibling = null;
316 - fiber.pendingProps = pendingProps;
317 - return fiber;
318 - }
294 + const clone = createWorkInProgress(fiber, pendingProps, expirationTime);
295 + clone.index = 0;
296 + clone.sibling = null;
297 + return clone;
298 }
299
300 function placeChild(
@@ -1553,11 +1532,8 @@ function ChildReconciler(shouldClone, shouldTrackSideEffects) {
1532 return reconcileChildFibers;
1533 }
1534
1556 -export const reconcileChildFibers = ChildReconciler(true, true);
1557 -
1558 -export const reconcileChildFibersInPlace = ChildReconciler(false, true);
1559 -
1560 -export const mountChildFibersInPlace = ChildReconciler(false, false);
1535 +export const reconcileChildFibers = ChildReconciler(true);
1536 +export const mountChildFibers = ChildReconciler(false);
1537
1538 export function cloneChildFibers(
1539 current: Fiber | null,
packages/react-reconciler/src/ReactFiberBeginWork.js
+11 -29
@@ -44,9 +44,8 @@ import {cancelWorkTimer} from './ReactDebugFiberPerf';
44
45 import ReactFiberClassComponent from './ReactFiberClassComponent';
46 import {
47 - mountChildFibersInPlace,
47 + mountChildFibers,
48 reconcileChildFibers,
49 - reconcileChildFibersInPlace,
49 cloneChildFibers,
50 } from './ReactChildFiber';
51 import {processUpdateQueue} from './ReactFiberUpdateQueue';
@@ -119,13 +118,13 @@ export default function<T, P, I, TI, HI, PI, C, CC, CX, PL>(
118 // won't update its child set by applying minimal side-effects. Instead,
119 // we will add them all to the child before it gets rendered. That means
120 // we can optimize this reconciliation pass by not tracking side-effects.
122 - workInProgress.child = mountChildFibersInPlace(
121 + workInProgress.child = mountChildFibers(
122 workInProgress,
124 - workInProgress.child,
123 + null,
124 nextChildren,
125 renderExpirationTime,
126 );
128 - } else if (current.child === workInProgress.child) {
127 + } else {
128 // If the current child is the same as the work in progress, it means that
129 // we haven't yet started any work on these children. Therefore, we use
130 // the clone algorithm to create a copy of all the current children.
@@ -134,17 +133,7 @@ export default function<T, P, I, TI, HI, PI, C, CC, CX, PL>(
133 // let's throw it out.
134 workInProgress.child = reconcileChildFibers(
135 workInProgress,
137 - workInProgress.child,
138 - nextChildren,
139 - renderExpirationTime,
140 - );
141 - } else {
142 - // If, on the other hand, it is already using a clone, that means we've
143 - // already begun some work on this tree and we can continue where we left
144 - // off by reconciling against the existing children.
145 - workInProgress.child = reconcileChildFibersInPlace(
146 - workInProgress,
147 - workInProgress.child,
136 + current.child,
137 nextChildren,
138 renderExpirationTime,
139 );
@@ -355,9 +344,9 @@ export default function<T, P, I, TI, HI, PI, C, CC, CX, PL>(
344 // Ensure that children mount into this root without tracking
345 // side-effects. This ensures that we don't store Placement effects on
346 // nodes that will be hydrated.
358 - workInProgress.child = mountChildFibersInPlace(
347 + workInProgress.child = mountChildFibers(
348 workInProgress,
360 - workInProgress.child,
349 + null,
350 element,
351 renderExpirationTime,
352 );
@@ -569,21 +558,14 @@ export default function<T, P, I, TI, HI, PI, C, CC, CX, PL>(
558 // The following is a fork of reconcileChildrenAtExpirationTime but using
559 // stateNode to store the child.
560 if (current === null) {
572 - workInProgress.stateNode = mountChildFibersInPlace(
573 - workInProgress,
574 - workInProgress.stateNode,
575 - nextChildren,
576 - renderExpirationTime,
577 - );
578 - } else if (current.child === workInProgress.child) {
579 - workInProgress.stateNode = reconcileChildFibers(
561 + workInProgress.stateNode = mountChildFibers(
562 workInProgress,
563 workInProgress.stateNode,
564 nextChildren,
565 renderExpirationTime,
566 );
567 } else {
586 - workInProgress.stateNode = reconcileChildFibersInPlace(
568 + workInProgress.stateNode = reconcileChildFibers(
569 workInProgress,
570 workInProgress.stateNode,
571 nextChildren,
@@ -628,9 +610,9 @@ export default function<T, P, I, TI, HI, PI, C, CC, CX, PL>(
610 // flow doesn't do during mount. This doesn't happen at the root because
611 // the root always starts with a "current" with a null child.
612 // TODO: Consider unifying this with how the root works.
631 - workInProgress.child = reconcileChildFibersInPlace(
613 + workInProgress.child = reconcileChildFibers(
614 workInProgress,
633 - workInProgress.child,
615 + null,
616 nextChildren,
617 renderExpirationTime,
618 );