@@ -72,7 +72,9 @@ import {
7272 NoFlags ,
7373 DidCapture ,
7474 Snapshot ,
75+ ChildDeletion ,
7576 StaticMask ,
77+ MutationMask ,
7678} from './ReactFiberFlags' ;
7779import invariant from 'shared/invariant' ;
7880
@@ -173,6 +175,31 @@ function markRef(workInProgress: Fiber) {
173175 workInProgress . flags |= Ref ;
174176}
175177
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+
176203let appendAllChildren ;
177204let updateHostContainer ;
178205let updateHostComponent ;
@@ -217,7 +244,7 @@ if (supportsMutation) {
217244 }
218245 } ;
219246
220- updateHostContainer = function ( workInProgress : Fiber ) {
247+ updateHostContainer = function ( current : null | Fiber , workInProgress : Fiber ) {
221248 // Noop
222249 } ;
223250 updateHostComponent = function (
@@ -461,13 +488,13 @@ if (supportsMutation) {
461488 node = node . sibling ;
462489 }
463490 } ;
464- updateHostContainer = function ( workInProgress : Fiber ) {
491+ updateHostContainer = function ( current : null | Fiber , workInProgress : Fiber ) {
465492 const portalOrRoot : {
466493 containerInfo : Container ,
467494 pendingChildren : ChildSet ,
468495 ...
469496 } = workInProgress . stateNode ;
470- const childrenUnchanged = workInProgress . firstEffect === null ;
497+ const childrenUnchanged = hadNoMutationsEffects ( current , workInProgress ) ;
471498 if ( childrenUnchanged ) {
472499 // No changes, just reuse the existing instance.
473500 } else {
@@ -492,7 +519,7 @@ if (supportsMutation) {
492519 const oldProps = current . memoizedProps ;
493520 // If there are no effects associated with this node, then none of our children had any updates.
494521 // This guarantees that we can reuse all of them.
495- const childrenUnchanged = workInProgress . firstEffect === null ;
522+ const childrenUnchanged = hadNoMutationsEffects ( current , workInProgress ) ;
496523 if ( childrenUnchanged && oldProps === newProps ) {
497524 // No changes, just reuse the existing instance.
498525 // Note that this might release a previous clone.
@@ -575,7 +602,7 @@ if (supportsMutation) {
575602 } ;
576603} else {
577604 // No host operations
578- updateHostContainer = function ( workInProgress : Fiber ) {
605+ updateHostContainer = function ( current : null | Fiber , workInProgress : Fiber ) {
579606 // Noop
580607 } ;
581608 updateHostComponent = function (
@@ -847,7 +874,7 @@ function completeWork(
847874 workInProgress . flags |= Snapshot ;
848875 }
849876 }
850- updateHostContainer ( workInProgress ) ;
877+ updateHostContainer ( current , workInProgress ) ;
851878 bubbleProperties ( workInProgress ) ;
852879 return null ;
853880 }
@@ -1142,7 +1169,7 @@ function completeWork(
11421169 }
11431170 case HostPortal :
11441171 popHostContainer ( workInProgress ) ;
1145- updateHostContainer ( workInProgress ) ;
1172+ updateHostContainer ( current , workInProgress ) ;
11461173 if ( current === null ) {
11471174 preparePortalMount ( workInProgress . stateNode . containerInfo ) ;
11481175 }
@@ -1226,11 +1253,7 @@ function completeWork(
12261253
12271254 // Rerender the whole list, but this time, we'll force fallbacks
12281255 // 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.
12341257 // Reset the child fibers to their original state.
12351258 workInProgress . subtreeFlags = NoFlags ;
12361259 resetChildFibers ( workInProgress , renderLanes ) ;
@@ -1301,15 +1324,6 @@ function completeWork(
13011324 ! renderedTail . alternate &&
13021325 ! getIsHydrating ( ) // We don't cut it if we're hydrating.
13031326 ) {
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- }
13131327 // We're done.
13141328 bubbleProperties ( workInProgress ) ;
13151329 return null ;
@@ -1369,7 +1383,6 @@ function completeWork(
13691383 const next = renderState . tail ;
13701384 renderState . rendering = next ;
13711385 renderState . tail = next . sibling ;
1372- renderState . lastEffect = workInProgress . lastEffect ;
13731386 renderState . renderingStartTime = now ( ) ;
13741387 next . sibling = null ;
13751388
0 commit comments