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) {