@samitouri / QOS-React-2 / commits / 57e8513a30

Refactor: remove early returns from updateFiberRecursively

This doesn't change any logic, just the shape of the control flow. I want to unify some branches at the end, so it's easier if there are no early returns.

Dan Abramov committed Apr 16, 2019 at 16:17 UTC 57e8513a300ea5b594d1bff8e659926eff2be409
1 file changed +78 -93
src/backend/renderer.js
+78 -93
@@ -909,7 +909,9 @@ export function attach(
909 if (__DEBUG__) {
910 debug('updateFiberRecursively()', nextFiber, parentFiber);
911 }
912 -
912 + const shouldIncludeInTree = !shouldFilterFiber(nextFiber);
913 + const isSuspense = nextFiber.tag === SuspenseComponent;
914 + let hasChildOrderChanged = false;
915 // The behavior of timed-out Suspense trees is unique.
916 // Rather than unmount the timed out content (and possibly lose important state),
917 // React re-parents this content within a hidden Fragment while the fallback is showing.
@@ -917,106 +919,89 @@ export function attach(
919 // It might even result in a bad user experience for e.g. node selection in the Elements panel.
920 // The easiest fix is to strip out the intermediate Fragment fibers,
921 // so the Elements panel and Profiler don't need to special case them.
920 - if (nextFiber.tag === SuspenseComponent) {
921 - // Suspense components only have a non-null memoizedState if they're timed-out.
922 - const prevDidTimeout = prevFiber.memoizedState !== null;
923 - const nextDidTimeOut = nextFiber.memoizedState !== null;
924 -
925 - // The logic below is inspired by the codepaths in updateSuspenseComponent()
926 - // inside ReactFiberBeginWork in the React source code.
927 - if (prevDidTimeout) {
928 - if (nextDidTimeOut) {
929 - // Fallback -> Fallback:
930 - // 1. Reconcile fallback set.
931 - const nextFallbackChildSet = nextFiber.child.sibling;
932 - // Note: We can't use nextFiber.child.sibling.alternate
933 - // because the set is special and alternate may not exist.
934 - const prevFallbackChildSet = prevFiber.child.sibling;
935 - updateFiberRecursively(
936 - nextFallbackChildSet,
937 - prevFallbackChildSet,
938 - nextFiber
939 - );
940 - return;
941 - } else {
942 - // Fallback -> Primary:
943 - // 1. Unmount fallback set
944 - // Note: don't emulate fallback unmount because React actually did it.
945 - // 2. Mount primary set
946 - const nextPrimaryChildSet = nextFiber.child;
947 - mountFiberRecursively(nextPrimaryChildSet, nextFiber, true);
948 - return;
949 - }
950 - } else {
951 - if (nextDidTimeOut) {
952 - // Primary -> Fallback:
953 - // 1. Hide primary set
954 - // This is not a real unmount, so it won't get reported by React.
955 - // By this point it's *too late* to find the previous primary child set
956 - // so we'll just tell the store to "forget" about those children.
957 - // They might "resurface" later when we switch to primary content,
958 - // but from the store's point of view they will be a new tree.
959 - recordRecursiveRemoveChildren(nextFiber);
960 - // 2. Mount fallback set
961 - const nextFallbackChildSet = nextFiber.child.sibling;
962 - mountFiberRecursively(nextFallbackChildSet, nextFiber, true);
963 - return;
964 - } else {
965 - // Primary -> Primary:
966 - // 1. Reconcile primary set.
967 - // Note: no return so we can passthrough to the logic below.
968 - }
969 - }
970 - }
971 -
972 - const shouldIncludeInTree = !shouldFilterFiber(nextFiber);
973 - let hasChildOrderChanged = false;
974 - if (nextFiber.child !== prevFiber.child) {
975 - // If the first child is different, we need to traverse them.
976 - // Each next child will be either a new child (mount) or an alternate (update).
977 - let nextChild = nextFiber.child;
978 - let prevChildAtSameIndex = prevFiber.child;
979 - while (nextChild) {
980 - // We already know children will be referentially different because
981 - // they are either new mounts or alternates of previous children.
982 - // Schedule updates and mounts depending on whether alternates exist.
983 - // We don't track deletions here because they are reported separately.
984 - if (nextChild.alternate) {
985 - const prevChild = nextChild.alternate;
986 - updateFiberRecursively(
987 - nextChild,
988 - prevChild,
989 - shouldIncludeInTree ? nextFiber : parentFiber
990 - );
991 - // However we also keep track if the order of the children matches
992 - // the previous order. They are always different referentially, but
993 - // if the instances line up conceptually we'll want to know that.
994 - if (!hasChildOrderChanged && prevChild !== prevChildAtSameIndex) {
922 + // Suspense components only have a non-null memoizedState if they're timed-out.
923 + const prevDidTimeout = isSuspense && prevFiber.memoizedState !== null;
924 + const nextDidTimeOut = isSuspense && nextFiber.memoizedState !== null;
925 + // The logic below is inspired by the codepaths in updateSuspenseComponent()
926 + // inside ReactFiberBeginWork in the React source code.
927 + if (prevDidTimeout && nextDidTimeOut) {
928 + // Fallback -> Fallback:
929 + // 1. Reconcile fallback set.
930 + const nextFallbackChildSet = nextFiber.child.sibling;
931 + // Note: We can't use nextFiber.child.sibling.alternate
932 + // because the set is special and alternate may not exist.
933 + const prevFallbackChildSet = prevFiber.child.sibling;
934 + updateFiberRecursively(
935 + nextFallbackChildSet,
936 + prevFallbackChildSet,
937 + nextFiber
938 + );
939 + } else if (prevDidTimeout && !nextDidTimeOut) {
940 + // Fallback -> Primary:
941 + // 1. Unmount fallback set
942 + // Note: don't emulate fallback unmount because React actually did it.
943 + // 2. Mount primary set
944 + const nextPrimaryChildSet = nextFiber.child;
945 + mountFiberRecursively(nextPrimaryChildSet, nextFiber, true);
946 + } else if (!prevDidTimeout && nextDidTimeOut) {
947 + // Primary -> Fallback:
948 + // 1. Hide primary set
949 + // This is not a real unmount, so it won't get reported by React.
950 + // By this point it's *too late* to find the previous primary child set
951 + // so we'll just tell the store to "forget" about those children.
952 + // They might "resurface" later when we switch to primary content,
953 + // but from the store's point of view they will be a new tree.
954 + recordRecursiveRemoveChildren(nextFiber);
955 + // 2. Mount fallback set
956 + const nextFallbackChildSet = nextFiber.child.sibling;
957 + mountFiberRecursively(nextFallbackChildSet, nextFiber, true);
958 + } else if (!prevDidTimeout && !nextDidTimeOut) {
959 + // Common case: Primary -> Primary.
960 + // This is the same codepath as for non-Suspense fibers.
961 + if (nextFiber.child !== prevFiber.child) {
962 + // If the first child is different, we need to traverse them.
963 + // Each next child will be either a new child (mount) or an alternate (update).
964 + let nextChild = nextFiber.child;
965 + let prevChildAtSameIndex = prevFiber.child;
966 + while (nextChild) {
967 + // We already know children will be referentially different because
968 + // they are either new mounts or alternates of previous children.
969 + // Schedule updates and mounts depending on whether alternates exist.
970 + // We don't track deletions here because they are reported separately.
971 + if (nextChild.alternate) {
972 + const prevChild = nextChild.alternate;
973 + updateFiberRecursively(
974 + nextChild,
975 + prevChild,
976 + shouldIncludeInTree ? nextFiber : parentFiber
977 + );
978 + // However we also keep track if the order of the children matches
979 + // the previous order. They are always different referentially, but
980 + // if the instances line up conceptually we'll want to know that.
981 + if (prevChild !== prevChildAtSameIndex) {
982 + hasChildOrderChanged = true;
983 + }
984 + } else {
985 + mountFiberRecursively(
986 + nextChild,
987 + shouldIncludeInTree ? nextFiber : parentFiber
988 + );
989 hasChildOrderChanged = true;
990 }
997 - } else {
998 - mountFiberRecursively(
999 - nextChild,
1000 - shouldIncludeInTree ? nextFiber : parentFiber
1001 - );
1002 - if (!hasChildOrderChanged) {
1003 - hasChildOrderChanged = true;
991 + // Try the next child.
992 + nextChild = nextChild.sibling;
993 + // Advance the pointer in the previous list so that we can
994 + // keep comparing if they line up.
995 + if (!hasChildOrderChanged && prevChildAtSameIndex != null) {
996 + prevChildAtSameIndex = prevChildAtSameIndex.sibling;
997 }
998 }
1006 - // Try the next child.
1007 - nextChild = nextChild.sibling;
1008 - // Advance the pointer in the previous list so that we can
1009 - // keep comparing if they line up.
999 + // If we have no more children, but used to, they don't line up.
1000 if (!hasChildOrderChanged && prevChildAtSameIndex != null) {
1011 - prevChildAtSameIndex = prevChildAtSameIndex.sibling;
1001 + hasChildOrderChanged = true;
1002 }
1003 }
1014 - // If we have no more children, but used to, they don't line up.
1015 - if (!hasChildOrderChanged && prevChildAtSameIndex != null) {
1016 - hasChildOrderChanged = true;
1017 - }
1004 }
1019 -
1005 if (shouldIncludeInTree) {
1006 const isProfilingSupported = nextFiber.hasOwnProperty('treeBaseDuration');
1007 if (isProfilingSupported) {