@@ -75,9 +75,10 @@ import {
7575 Hydrating ,
7676 HydratingAndUpdate ,
7777 Passive ,
78+ BeforeMutationMask ,
7879 MutationMask ,
79- PassiveMask ,
8080 LayoutMask ,
81+ PassiveMask ,
8182 PassiveUnmountPendingDev ,
8283} from './ReactFiberFlags' ;
8384import getComponentName from 'shared/getComponentName' ;
@@ -128,6 +129,8 @@ import {
128129 commitHydratedSuspenseInstance ,
129130 clearContainer ,
130131 prepareScopeUpdate ,
132+ prepareForCommit ,
133+ beforeActiveInstanceBlur ,
131134} from './ReactFiberHostConfig' ;
132135import {
133136 captureCommitPhaseError ,
@@ -144,6 +147,7 @@ import {
144147 Passive as HookPassive ,
145148} from './ReactHookEffectTags' ;
146149import { didWarnAboutReassigningProps } from './ReactFiberBeginWork.new' ;
150+ import { doesFiberContain } from './ReactFiberTreeReflection' ;
147151
148152let didWarnAboutUndefinedSnapshotBeforeUpdate : Set < mixed > | null = null ;
149153if ( __DEV__ ) {
@@ -259,18 +263,114 @@ function safelyCallDestroy(current: Fiber, destroy: () => void) {
259263 }
260264}
261265
262- function commitBeforeMutationLifeCycles (
263- current : Fiber | null ,
264- finishedWork : Fiber ,
265- ) : void {
266- switch ( finishedWork . tag ) {
267- case FunctionComponent :
268- case ForwardRef :
269- case SimpleMemoComponent : {
266+ let focusedInstanceHandle : null | Fiber = null ;
267+ let shouldFireAfterActiveInstanceBlur : boolean = false ;
268+
269+ export function commitBeforeMutationEffects (
270+ root : FiberRoot ,
271+ firstChild : Fiber ,
272+ ) {
273+ focusedInstanceHandle = prepareForCommit ( root . containerInfo ) ;
274+
275+ nextEffect = firstChild ;
276+ commitBeforeMutationEffects_begin ( ) ;
277+
278+ // We no longer need to track the active instance fiber
279+ const shouldFire = shouldFireAfterActiveInstanceBlur ;
280+ shouldFireAfterActiveInstanceBlur = false ;
281+ focusedInstanceHandle = null ;
282+
283+ return shouldFire ;
284+ }
285+
286+ function commitBeforeMutationEffects_begin ( ) {
287+ while ( nextEffect !== null ) {
288+ const fiber = nextEffect ;
289+
290+ // TODO: Should wrap this in flags check, too, as optimization
291+ const deletions = fiber . deletions ;
292+ if ( deletions !== null ) {
293+ for ( let i = 0 ; i < deletions . length ; i ++ ) {
294+ const deletion = deletions [ i ] ;
295+ commitBeforeMutationEffectsDeletion ( deletion ) ;
296+ }
297+ }
298+
299+ const child = fiber . child ;
300+ if (
301+ ( fiber . subtreeFlags & BeforeMutationMask ) !== NoFlags &&
302+ child !== null
303+ ) {
304+ ensureCorrectReturnPointer ( child , fiber ) ;
305+ nextEffect = child ;
306+ } else {
307+ commitBeforeMutationEffects_complete ( ) ;
308+ }
309+ }
310+ }
311+
312+ function commitBeforeMutationEffects_complete ( ) {
313+ while ( nextEffect !== null ) {
314+ const fiber = nextEffect ;
315+ if ( __DEV__ ) {
316+ setCurrentDebugFiberInDEV ( fiber ) ;
317+ invokeGuardedCallback (
318+ null ,
319+ commitBeforeMutationEffectsOnFiber ,
320+ null ,
321+ fiber ,
322+ ) ;
323+ if ( hasCaughtError ( ) ) {
324+ const error = clearCaughtError ( ) ;
325+ captureCommitPhaseError ( fiber , error ) ;
326+ }
327+ resetCurrentDebugFiberInDEV ( ) ;
328+ } else {
329+ try {
330+ commitBeforeMutationEffectsOnFiber ( fiber ) ;
331+ } catch ( error ) {
332+ captureCommitPhaseError ( fiber , error ) ;
333+ }
334+ }
335+
336+ const sibling = fiber . sibling ;
337+ if ( sibling !== null ) {
338+ ensureCorrectReturnPointer ( sibling , fiber . return ) ;
339+ nextEffect = sibling ;
270340 return ;
271341 }
272- case ClassComponent : {
273- if ( finishedWork . flags & Snapshot ) {
342+
343+ nextEffect = fiber . return ;
344+ }
345+ }
346+
347+ function commitBeforeMutationEffectsOnFiber ( finishedWork : Fiber ) {
348+ const current = finishedWork . alternate ;
349+ const flags = finishedWork . flags ;
350+
351+ if ( ! shouldFireAfterActiveInstanceBlur && focusedInstanceHandle !== null ) {
352+ // Check to see if the focused element was inside of a hidden (Suspense) subtree.
353+ // TODO: Move this out of the hot path using a dedicated effect tag.
354+ if (
355+ finishedWork . tag === SuspenseComponent &&
356+ isSuspenseBoundaryBeingHidden ( current , finishedWork ) &&
357+ doesFiberContain ( finishedWork , focusedInstanceHandle )
358+ ) {
359+ shouldFireAfterActiveInstanceBlur = true ;
360+ beforeActiveInstanceBlur ( finishedWork ) ;
361+ }
362+ }
363+
364+ if ( ( flags & Snapshot ) !== NoFlags ) {
365+ setCurrentDebugFiberInDEV ( finishedWork ) ;
366+
367+ switch ( finishedWork . tag ) {
368+ case FunctionComponent :
369+ case ForwardRef :
370+ case SimpleMemoComponent : {
371+ break ;
372+ }
373+ case ClassComponent : {
274374 if ( current !== null ) {
275375 const prevProps = current . memoizedProps ;
276376 const prevState = current . memoizedState ;
@@ -324,30 +424,43 @@ function commitBeforeMutationLifeCycles(
324424 }
325425 instance . __reactInternalSnapshotBeforeUpdate = snapshot ;
326426 }
427+ break ;
327428 }
328- return ;
329- }
330- case HostRoot : {
331- if ( supportsMutation ) {
332- if ( finishedWork . flags & Snapshot ) {
429+ case HostRoot : {
430+ if ( supportsMutation ) {
333431 const root = finishedWork . stateNode ;
334432 clearContainer ( root . containerInfo ) ;
335433 }
434+ break ;
435+ }
436+ case HostComponent :
437+ case HostText :
438+ case HostPortal :
439+ case IncompleteClassComponent :
440+ // Nothing to do for these component types
441+ break ;
442+ default : {
443+ invariant (
444+ false ,
445+ 'This unit of work tag should not have side-effects. This error is ' +
446+ 'likely caused by a bug in React. Please file an issue.' ,
447+ ) ;
336448 }
337- return ;
338449 }
339- case HostComponent :
340- case HostText :
341- case HostPortal :
342- case IncompleteClassComponent :
343- // Nothing to do for these component types
344- return ;
450+
451+ resetCurrentDebugFiberInDEV ( ) ;
452+ }
453+ }
454+
455+ function commitBeforeMutationEffectsDeletion ( deletion : Fiber ) {
456+ // TODO (effects) It would be nice to avoid calling doesFiberContain()
457+ // Maybe we can repurpose one of the subtreeFlags positions for this instead?
458+ // Use it to store which part of the tree the focused instance is in?
459+ // This assumes we can safely determine that instance during the "render" phase.
460+ if ( doesFiberContain ( deletion , ( ( focusedInstanceHandle : any ) : Fiber ) ) ) {
461+ shouldFireAfterActiveInstanceBlur = true ;
462+ beforeActiveInstanceBlur ( deletion ) ;
345463 }
346- invariant (
347- false ,
348- 'This unit of work tag should not have side-effects. This error is ' +
349- 'likely caused by a bug in React. Please file an issue.' ,
350- ) ;
351464}
352465
353466function commitHookEffectListUnmount ( flags : HookFlags , finishedWork : Fiber ) {
@@ -2353,7 +2466,6 @@ function ensureCorrectReturnPointer(fiber, expectedReturnFiber) {
23532466}
23542467
23552468export {
2356- commitBeforeMutationLifeCycles ,
23572469 commitResetTextContent ,
23582470 commitPlacement ,
23592471 commitDeletion ,
0 commit comments