Delete remaining references to effect list (#20625)
I think that's it!
Andrew Clark committed
Jan 20, 2021 at 12:24 UTC
fceb75e8995eaa6eb5c996111dc2bf7d8679cd99
10 files changed
+63
-167
.eslintrc.js
+1
-1
@@ -116,7 +116,7 @@ module.exports = {
116
'react-internal/no-cross-fork-types': [
117
ERROR,
118
{
119
- old: [],
119
+ old: ['firstEffect', 'nextEffect'],
120
new: [],
121
},
122
],
packages/react-reconciler/src/ReactChildFiber.new.js
-14
@@ -263,20 +263,6 @@ function ChildReconciler(shouldTrackSideEffects) {
263
// Noop.
264
return;
265
}
266
- // Deletions are added in reversed order so we add it to the front.
267
- // At this point, the return fiber's effect list is empty except for
268
- // deletions, so we can just append the deletion to the list. The remaining
269
- // effects aren't added until the complete phase. Once we implement
270
- // resuming, this may not be true.
271
- const last = returnFiber.lastEffect;
272
- if (last !== null) {
273
- last.nextEffect = childToDelete;
274
- returnFiber.lastEffect = childToDelete;
275
- } else {
276
- returnFiber.firstEffect = returnFiber.lastEffect = childToDelete;
277
- }
278
- childToDelete.nextEffect = null;
279
-
266
const deletions = returnFiber.deletions;
267
if (deletions === null) {
268
returnFiber.deletions = [childToDelete];
packages/react-reconciler/src/ReactFiber.new.js
+5
-15
@@ -144,10 +144,6 @@ function FiberNode(
144
145
// Effects
146
this.flags = NoFlags;
147
- this.nextEffect = null;
148
-
149
- this.firstEffect = null;
150
- this.lastEffect = null;
147
this.subtreeFlags = NoFlags;
148
this.deletions = null;
149
@@ -285,10 +281,7 @@ export function createWorkInProgress(current: Fiber, pendingProps: any): Fiber {
281
// Reset the effect tag.
282
workInProgress.flags = NoFlags;
283
288
- // The effect list is no longer valid.
289
- workInProgress.nextEffect = null;
290
- workInProgress.firstEffect = null;
291
- workInProgress.lastEffect = null;
284
+ // The effects are no longer valid.
285
workInProgress.subtreeFlags = NoFlags;
286
workInProgress.deletions = null;
287
@@ -370,10 +363,7 @@ export function resetWorkInProgress(workInProgress: Fiber, renderLanes: Lanes) {
363
// that child fiber is setting, not the reconciliation.
364
workInProgress.flags &= StaticMask | Placement;
365
373
- // The effect list is no longer valid.
374
- workInProgress.nextEffect = null;
375
- workInProgress.firstEffect = null;
376
- workInProgress.lastEffect = null;
366
+ // The effects are no longer valid.
367
368
const current = workInProgress.alternate;
369
if (current === null) {
@@ -403,6 +393,9 @@ export function resetWorkInProgress(workInProgress: Fiber, renderLanes: Lanes) {
393
workInProgress.lanes = current.lanes;
394
395
workInProgress.child = current.child;
396
+ // TODO: `subtreeFlags` should be reset to NoFlags, like we do in
397
+ // `createWorkInProgress`. Nothing reads this until the complete phase,
398
+ // currently, but it might in the future, and we should be consistent.
399
workInProgress.subtreeFlags = current.subtreeFlags;
400
workInProgress.deletions = null;
401
workInProgress.memoizedProps = current.memoizedProps;
@@ -847,9 +840,6 @@ export function assignFiberPropertiesInDEV(
840
target.dependencies = source.dependencies;
841
target.mode = source.mode;
842
target.flags = source.flags;
850
- target.nextEffect = source.nextEffect;
851
- target.firstEffect = source.firstEffect;
852
- target.lastEffect = source.lastEffect;
843
target.subtreeFlags = source.subtreeFlags;
844
target.deletions = source.deletions;
845
target.lanes = source.lanes;
packages/react-reconciler/src/ReactFiberBeginWork.new.js
+3
-33
@@ -2198,8 +2198,6 @@ function updateSuspensePrimaryChildren(
2198
primaryChildFragment.sibling = null;
2199
if (currentFallbackChildFragment !== null) {
2200
// Delete the fallback child fragment
2201
- currentFallbackChildFragment.nextEffect = null;
2202
- workInProgress.firstEffect = workInProgress.lastEffect = currentFallbackChildFragment;
2201
const deletions = workInProgress.deletions;
2202
if (deletions === null) {
2203
workInProgress.deletions = [currentFallbackChildFragment];
@@ -2261,22 +2259,9 @@ function updateSuspenseFallbackChildren(
2259
currentPrimaryChildFragment.treeBaseDuration;
2260
}
2261
2264
- if (currentFallbackChildFragment !== null) {
2265
- // The fallback fiber was added as a deletion effect during the first
2266
- // pass. However, since we're going to remain on the fallback, we no
2267
- // longer want to delete it. So we need to remove it from the list.
2268
- // Deletions are stored on the same list as effects, and are always added
2269
- // to the front. So we know that the first effect must be the fallback
2270
- // deletion effect, and everything after that is from the primary free.
2271
- const firstPrimaryTreeEffect = currentFallbackChildFragment.nextEffect;
2272
- if (firstPrimaryTreeEffect !== null) {
2273
- workInProgress.firstEffect = firstPrimaryTreeEffect;
2274
- } else {
2275
- // TODO: Reset this somewhere else? Lol legacy mode is so weird.
2276
- workInProgress.firstEffect = workInProgress.lastEffect = null;
2277
- }
2278
- }
2279
-
2262
+ // The fallback fiber was added as a deletion during the first pass.
2263
+ // However, since we're going to remain on the fallback, we no longer want
2264
+ // to delete it.
2265
workInProgress.deletions = null;
2266
} else {
2267
primaryChildFragment = createWorkInProgressOffscreenFiber(
@@ -2773,7 +2758,6 @@ function initSuspenseListRenderState(
2758
tail: null | Fiber,
2759
lastContentRow: null | Fiber,
2760
tailMode: SuspenseListTailMode,
2776
- lastEffectBeforeRendering: null | Fiber,
2761
): void {
2762
const renderState: null | SuspenseListRenderState =
2763
workInProgress.memoizedState;
@@ -2785,7 +2769,6 @@ function initSuspenseListRenderState(
2769
last: lastContentRow,
2770
tail: tail,
2771
tailMode: tailMode,
2788
- lastEffect: lastEffectBeforeRendering,
2772
}: SuspenseListRenderState);
2773
} else {
2774
// We can reuse the existing object from previous renders.
@@ -2795,7 +2778,6 @@ function initSuspenseListRenderState(
2778
renderState.last = lastContentRow;
2779
renderState.tail = tail;
2780
renderState.tailMode = tailMode;
2798
- renderState.lastEffect = lastEffectBeforeRendering;
2781
}
2782
}
2783
@@ -2877,7 +2859,6 @@ function updateSuspenseListComponent(
2859
tail,
2860
lastContentRow,
2861
tailMode,
2880
- workInProgress.lastEffect,
2862
);
2863
break;
2864
}
@@ -2909,7 +2890,6 @@ function updateSuspenseListComponent(
2890
tail,
2891
null, // last
2892
tailMode,
2912
- workInProgress.lastEffect,
2893
);
2894
break;
2895
}
@@ -2920,7 +2900,6 @@ function updateSuspenseListComponent(
2900
null, // tail
2901
null, // last
2902
undefined,
2923
- workInProgress.lastEffect,
2903
);
2904
break;
2905
}
@@ -3180,15 +3159,6 @@ function remountFiber(
3159
3160
// Delete the old fiber and place the new one.
3161
// Since the old fiber is disconnected, we have to schedule it manually.
3183
- const last = returnFiber.lastEffect;
3184
- if (last !== null) {
3185
- last.nextEffect = current;
3186
- returnFiber.lastEffect = current;
3187
- } else {
3188
- returnFiber.firstEffect = returnFiber.lastEffect = current;
3189
- }
3190
- current.nextEffect = null;
3191
-
3162
const deletions = returnFiber.deletions;
3163
if (deletions === null) {
3164
returnFiber.deletions = [current];
packages/react-reconciler/src/ReactFiberCommitWork.new.js
-3
@@ -1213,9 +1213,6 @@ export function detachFiberAfterEffects(fiber: Fiber): void {
1213
fiber.sibling = null;
1214
fiber.stateNode = null;
1215
fiber.updateQueue = null;
1216
- fiber.nextEffect = null;
1217
- fiber.firstEffect = null;
1218
- fiber.lastEffect = null;
1216
1217
if (__DEV__) {
1218
fiber._debugOwner = null;
packages/react-reconciler/src/ReactFiberCompleteWork.new.js
+35
-22
@@ -72,7 +72,9 @@ import {
72
NoFlags,
73
DidCapture,
74
Snapshot,
75
+ ChildDeletion,
76
StaticMask,
77
+ MutationMask,
78
} from './ReactFiberFlags';
79
import invariant from 'shared/invariant';
80
@@ -173,6 +175,31 @@ function markRef(workInProgress: Fiber) {
175
workInProgress.flags |= Ref;
176
}
177
178
+function hadNoMutationsEffects(current: null | Fiber, completedWork: Fiber) {
179
+ const didBailout = current !== null && current.child === completedWork.child;
180
+ if (didBailout) {
181
+ return true;
182
+ }
183
+
184
+ if ((completedWork.flags & ChildDeletion) !== NoFlags) {
185
+ return false;
186
+ }
187
+
188
+ // TODO: If we move the `hadNoMutationsEffects` call after `bubbleProperties`
189
+ // then we only have to check the `completedWork.subtreeFlags`.
190
+ let child = completedWork.child;
191
+ while (child !== null) {
192
+ if (
193
+ (child.flags & MutationMask) !== NoFlags ||
194
+ (child.subtreeFlags & MutationMask) !== NoFlags
195
+ ) {
196
+ return false;
197
+ }
198
+ child = child.sibling;
199
+ }
200
+ return true;
201
+}
202
+
203
let appendAllChildren;
204
let updateHostContainer;
205
let updateHostComponent;
@@ -217,7 +244,7 @@ if (supportsMutation) {
244
}
245
};
246
220
- updateHostContainer = function(workInProgress: Fiber) {
247
+ updateHostContainer = function(current: null | Fiber, workInProgress: Fiber) {
248
// Noop
249
};
250
updateHostComponent = function(
@@ -461,13 +488,13 @@ if (supportsMutation) {
488
node = node.sibling;
489
}
490
};
464
- updateHostContainer = function(workInProgress: Fiber) {
491
+ updateHostContainer = function(current: null | Fiber, workInProgress: Fiber) {
492
const portalOrRoot: {
493
containerInfo: Container,
494
pendingChildren: ChildSet,
495
...
496
} = workInProgress.stateNode;
470
- const childrenUnchanged = workInProgress.firstEffect === null;
497
+ const childrenUnchanged = hadNoMutationsEffects(current, workInProgress);
498
if (childrenUnchanged) {
499
// No changes, just reuse the existing instance.
500
} else {
@@ -492,7 +519,7 @@ if (supportsMutation) {
519
const oldProps = current.memoizedProps;
520
// If there are no effects associated with this node, then none of our children had any updates.
521
// This guarantees that we can reuse all of them.
495
- const childrenUnchanged = workInProgress.firstEffect === null;
522
+ const childrenUnchanged = hadNoMutationsEffects(current, workInProgress);
523
if (childrenUnchanged && oldProps === newProps) {
524
// No changes, just reuse the existing instance.
525
// Note that this might release a previous clone.
@@ -575,7 +602,7 @@ if (supportsMutation) {
602
};
603
} else {
604
// No host operations
578
- updateHostContainer = function(workInProgress: Fiber) {
605
+ updateHostContainer = function(current: null | Fiber, workInProgress: Fiber) {
606
// Noop
607
};
608
updateHostComponent = function(
@@ -847,7 +874,7 @@ function completeWork(
874
workInProgress.flags |= Snapshot;
875
}
876
}
850
- updateHostContainer(workInProgress);
877
+ updateHostContainer(current, workInProgress);
878
bubbleProperties(workInProgress);
879
return null;
880
}
@@ -1142,7 +1169,7 @@ function completeWork(
1169
}
1170
case HostPortal:
1171
popHostContainer(workInProgress);
1145
- updateHostContainer(workInProgress);
1172
+ updateHostContainer(current, workInProgress);
1173
if (current === null) {
1174
preparePortalMount(workInProgress.stateNode.containerInfo);
1175
}
@@ -1226,11 +1253,7 @@ function completeWork(
1253
1254
// Rerender the whole list, but this time, we'll force fallbacks
1255
// to stay in place.
1229
- // Reset the effect list before doing the second pass since that's now invalid.
1230
- if (renderState.lastEffect === null) {
1231
- workInProgress.firstEffect = null;
1232
- }
1233
- workInProgress.lastEffect = renderState.lastEffect;
1256
+ // Reset the effect flags before doing the second pass since that's now invalid.
1257
// Reset the child fibers to their original state.
1258
workInProgress.subtreeFlags = NoFlags;
1259
resetChildFibers(workInProgress, renderLanes);
@@ -1301,15 +1324,6 @@ function completeWork(
1324
!renderedTail.alternate &&
1325
!getIsHydrating() // We don't cut it if we're hydrating.
1326
) {
1304
- // We need to delete the row we just rendered.
1305
- // Reset the effect list to what it was before we rendered this
1306
- // child. The nested children have already appended themselves.
1307
- const lastEffect = (workInProgress.lastEffect =
1308
- renderState.lastEffect);
1309
- // Remove any effects that were appended after this point.
1310
- if (lastEffect !== null) {
1311
- lastEffect.nextEffect = null;
1312
- }
1327
// We're done.
1328
bubbleProperties(workInProgress);
1329
return null;
@@ -1369,7 +1383,6 @@ function completeWork(
1383
const next = renderState.tail;
1384
renderState.rendering = next;
1385
renderState.tail = next.sibling;
1372
- renderState.lastEffect = workInProgress.lastEffect;
1386
renderState.renderingStartTime = now();
1387
next.sibling = null;
1388
packages/react-reconciler/src/ReactFiberHydrationContext.new.js
-12
@@ -125,18 +125,6 @@ function deleteHydratableInstance(
125
childToDelete.stateNode = instance;
126
childToDelete.return = returnFiber;
127
128
- // This might seem like it belongs on progressedFirstDeletion. However,
129
- // these children are not part of the reconciliation list of children.
130
- // Even if we abort and rereconcile the children, that will try to hydrate
131
- // again and the nodes are still in the host tree so these will be
132
- // recreated.
133
- if (returnFiber.lastEffect !== null) {
134
- returnFiber.lastEffect.nextEffect = childToDelete;
135
- returnFiber.lastEffect = childToDelete;
136
- } else {
137
- returnFiber.firstEffect = returnFiber.lastEffect = childToDelete;
138
- }
139
-
128
const deletions = returnFiber.deletions;
129
if (deletions === null) {
130
returnFiber.deletions = [childToDelete];
packages/react-reconciler/src/ReactFiberSuspenseComponent.new.js
-3
@@ -60,9 +60,6 @@ export type SuspenseListRenderState = {|
60
tail: null | Fiber,
61
// Tail insertions setting.
62
tailMode: SuspenseListTailMode,
63
- // Last Effect before we rendered the "rendering" item.
64
- // Used to remove new effects added by the rendered item.
65
- lastEffect: null | Fiber,
63
|};
64
65
export function shouldCaptureSuspense(
packages/react-reconciler/src/ReactFiberThrow.new.js
-2
@@ -188,8 +188,6 @@ function throwException(
188
) {
189
// The source fiber did not complete.
190
sourceFiber.flags |= Incomplete;
191
- // Its effect list is no longer valid.
192
- sourceFiber.firstEffect = sourceFiber.lastEffect = null;
191
192
if (
193
value !== null &&
packages/react-reconciler/src/ReactFiberWorkLoop.new.js
+19
-62
@@ -117,14 +117,15 @@ import {
117
import {LegacyRoot} from './ReactRootTags';
118
import {
119
NoFlags,
120
- PerformedWork,
120
Placement,
121
PassiveStatic,
122
Incomplete,
123
HostEffectMask,
124
Hydrating,
125
+ BeforeMutationMask,
126
+ MutationMask,
127
+ LayoutMask,
128
PassiveMask,
127
- StaticMask,
129
} from './ReactFiberFlags';
130
import {
131
NoLanePriority,
@@ -1716,45 +1717,6 @@ function completeUnitOfWork(unitOfWork: Fiber): void {
1717
workInProgress = next;
1718
return;
1719
}
1719
-
1720
- if (
1721
- returnFiber !== null &&
1722
- // Do not append effects to parents if a sibling failed to complete
1723
- (returnFiber.flags & Incomplete) === NoFlags
1724
- ) {
1725
- // Append all the effects of the subtree and this fiber onto the effect
1726
- // list of the parent. The completion order of the children affects the
1727
- // side-effect order.
1728
- if (returnFiber.firstEffect === null) {
1729
- returnFiber.firstEffect = completedWork.firstEffect;
1730
- }
1731
- if (completedWork.lastEffect !== null) {
1732
- if (returnFiber.lastEffect !== null) {
1733
- returnFiber.lastEffect.nextEffect = completedWork.firstEffect;
1734
- }
1735
- returnFiber.lastEffect = completedWork.lastEffect;
1736
- }
1737
-
1738
- // If this fiber had side-effects, we append it AFTER the children's
1739
- // side-effects. We can perform certain side-effects earlier if needed,
1740
- // by doing multiple passes over the effect list. We don't want to
1741
- // schedule our own side-effect on our own list because if end up
1742
- // reusing children we'll schedule this effect onto itself since we're
1743
- // at the end.
1744
- const flags = completedWork.flags;
1745
-
1746
- // Skip both NoWork and PerformedWork tags when creating the effect
1747
- // list. PerformedWork effect is read by React DevTools but shouldn't be
1748
- // committed.
1749
- if ((flags & ~StaticMask) > PerformedWork) {
1750
- if (returnFiber.lastEffect !== null) {
1751
- returnFiber.lastEffect.nextEffect = completedWork;
1752
- } else {
1753
- returnFiber.firstEffect = completedWork;
1754
- }
1755
- returnFiber.lastEffect = completedWork;
1756
- }
1757
- }
1720
} else {
1721
// This fiber did not complete because something threw. Pop values off
1722
// the stack without entering the complete phase. If this is a boundary,
@@ -1791,8 +1753,7 @@ function completeUnitOfWork(unitOfWork: Fiber): void {
1753
}
1754
1755
if (returnFiber !== null) {
1794
- // Mark the parent fiber as incomplete and clear its effect list.
1795
- returnFiber.firstEffect = returnFiber.lastEffect = null;
1756
+ // Mark the parent fiber as incomplete and clear its subtree flags.
1757
returnFiber.flags |= Incomplete;
1758
returnFiber.subtreeFlags = NoFlags;
1759
returnFiber.deletions = null;
@@ -1910,24 +1871,6 @@ function commitRootImpl(root, renderPriorityLevel) {
1871
// times out.
1872
}
1873
1913
- // Get the list of effects.
1914
- let firstEffect;
1915
- if (finishedWork.flags > PerformedWork) {
1916
- // A fiber's effect list consists only of its children, not itself. So if
1917
- // the root has an effect, we need to add it to the end of the list. The
1918
- // resulting list is the set that would belong to the root's parent, if it
1919
- // had one; that is, all the effects in the tree including the root.
1920
- if (finishedWork.lastEffect !== null) {
1921
- finishedWork.lastEffect.nextEffect = finishedWork;
1922
- firstEffect = finishedWork.firstEffect;
1923
- } else {
1924
- firstEffect = finishedWork;
1925
- }
1926
- } else {
1927
- // There is no effect on the root.
1928
- firstEffect = finishedWork.firstEffect;
1929
- }
1930
-
1874
// If there are pending passive effects, schedule a callback to process them.
1875
// Do this as early as possible, so it is queued before anything else that
1876
// might get scheduled in the commit phase. (See #16714.)
@@ -1946,7 +1889,21 @@ function commitRootImpl(root, renderPriorityLevel) {
1889
}
1890
}
1891
1949
- if (firstEffect !== null) {
1892
+ // Check if there are any effects in the whole tree.
1893
+ // TODO: This is left over from the effect list implementation, where we had
1894
+ // to check for the existence of `firstEffect` to satsify Flow. I think the
1895
+ // only other reason this optimization exists is because it affects profiling.
1896
+ // Reconsider whether this is necessary.
1897
+ const subtreeHasEffects =
1898
+ (finishedWork.subtreeFlags &
1899
+ (BeforeMutationMask | MutationMask | LayoutMask | PassiveMask)) !==
1900
+ NoFlags;
1901
+ const rootHasEffect =
1902
+ (finishedWork.flags &
1903
+ (BeforeMutationMask | MutationMask | LayoutMask | PassiveMask)) !==
1904
+ NoFlags;
1905
+
1906
+ if (subtreeHasEffects || rootHasEffect) {
1907
let previousLanePriority;
1908
if (decoupleUpdatePriorityFromScheduler) {
1909
previousLanePriority = getCurrentUpdateLanePriority();