diff --git a/src/renderers/shared/fiber/ReactFiberTreeReflection.js b/src/renderers/shared/fiber/ReactFiberTreeReflection.js index 4e81d23a71d34..c7a51b036dd82 100644 --- a/src/renderers/shared/fiber/ReactFiberTreeReflection.js +++ b/src/renderers/shared/fiber/ReactFiberTreeReflection.js @@ -101,17 +101,16 @@ function findCurrentFiberUsingSlowPath(fiber : Fiber) : Fiber | null { let b = alternate; while (true) { let parentA = a.return; - let parentB = b.return; + let parentB = parentA ? parentA.alternate : null; if (!parentA || !parentB) { // We're at the root. break; } + + // If both copies of the parent fiber point to the same child, we can + // assume that the child is current. This happens when we bailout on low + // priority: the bailed out fiber's child reuses the current child. if (parentA.child === parentB.child) { - // If both parents are the same, then that is the current parent. If - // they're different but point to the same child, then it doesn't matter. - // Regardless, whatever child they point to is the current child. - // So we can now determine which child is current by scanning the child - // list for either A or B. let child = parentA.child; while (child) { if (child === a) { @@ -133,8 +132,63 @@ function findCurrentFiberUsingSlowPath(fiber : Fiber) : Fiber | null { 'Unable to find node on an unmounted component.' ); } - a = parentA; - b = parentB; + + if (a.return !== b.return) { + // The return pointer of A and the return pointer of B point to different + // fibers. We assume that return pointers never criss-cross, so A must + // belong to the child set of A.return, and B must belong to the child + // set of B.return. + a = parentA; + b = parentB; + } else { + // The return pointers pointer to the same fiber. We'll have to use the + // default, slow path: scan the child sets of each parent alternate to see + // which child belongs to which set. + // + // Search parent A's child set + let didFindChild = false; + let child = parentA.child; + while (child) { + if (child === a) { + didFindChild = true; + a = parentA; + b = parentB; + break; + } + if (child === b) { + didFindChild = true; + b = parentA; + a = parentB; + break; + } + child = child.sibling; + } + if (!didFindChild) { + // Search parent B's child set + child = parentB.child; + while (child) { + if (child === a) { + didFindChild = true; + a = parentB; + b = parentA; + break; + } + if (child === b) { + didFindChild = true; + b = parentB; + a = parentA; + break; + } + child = child.sibling; + } + invariant( + didFindChild, + 'Child was not found in either parent set. This indicates a bug ' + + 'related to the return pointer.' + ); + } + } + invariant( a.alternate === b, 'Return fibers should always be each others\' alternates.'