@samitouri / QOS-React-2 / commits / fceb75e899

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();