From 97f6786cba9cf1917ccc28d839a415c6a4c92581 Mon Sep 17 00:00:00 2001 From: Andrew Clark Date: Mon, 30 Jan 2017 18:41:25 -0800 Subject: [PATCH 1/2] Fix findDOMNode algorithm --- .../shared/fiber/ReactFiberTreeReflection.js | 63 ++++++++++++++++--- 1 file changed, 55 insertions(+), 8 deletions(-) diff --git a/src/renderers/shared/fiber/ReactFiberTreeReflection.js b/src/renderers/shared/fiber/ReactFiberTreeReflection.js index 4e81d23a71d3..c27f7ee97bd5 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,56 @@ 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) { + a = parentB; + b = parentA; + break; + } + if (child === b) { + b = parentB; + a = parentA; + break; + } + child = child.sibling; + } + } + } + invariant( a.alternate === b, 'Return fibers should always be each others\' alternates.' From ba5b9c0af3d66a89b403edd05d94bc0470d84fea Mon Sep 17 00:00:00 2001 From: Andrew Clark Date: Tue, 31 Jan 2017 11:35:10 -0800 Subject: [PATCH 2/2] Add invariant Defensive coding to prevent an infinite loop. --- src/renderers/shared/fiber/ReactFiberTreeReflection.js | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/renderers/shared/fiber/ReactFiberTreeReflection.js b/src/renderers/shared/fiber/ReactFiberTreeReflection.js index c27f7ee97bd5..c7a51b036dd8 100644 --- a/src/renderers/shared/fiber/ReactFiberTreeReflection.js +++ b/src/renderers/shared/fiber/ReactFiberTreeReflection.js @@ -168,17 +168,24 @@ function findCurrentFiberUsingSlowPath(fiber : Fiber) : Fiber | null { 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.' + ); } }