How to use captureCommitPhaseError method in Playwright Internal

Best JavaScript code snippet using playwright-internal

Run Playwright Internal automation tests on LambdaTest cloud grid

Perform automation testing on 3000+ real desktop and mobile devices online.

renderer.js

Source: renderer.js Github

copy
1// commitRootImpl方法
2
3// before mutation之前
4
5do {
6    // 触发useEffect回调与其他同步任务。由于这些任务可能触发新的渲染,所以这里要一直遍历执行直到没有任务
7    flushPassiveEffects();
8  } while (rootWithPendingPassiveEffects !== null);
9
10  // root指 fiberRootNode
11  // root.finishedWork指当前应用的rootFiber? TODO
12  const finishedWork = root.finishedWork;
13
14  // 凡是变量名带lane的都是优先级相关
15  const lanes = root.finishedLanes;
16  if (finishedWork === null) {
17    return null;
18  }
19  root.finishedWork = null;
20  root.finishedLanes = NoLanes;
21
22  // 重置Scheduler绑定的回调函数
23  root.callbackNode = null;
24  root.callbackId = NoLanes;
25
26  let remainingLanes = mergeLanes(finishedWork.lanes, finishedWork.childLanes);
27  // 重置优先级相关变量
28  markRootFinished(root, remainingLanes);
29
30  // 清除已完成的discrete updates,例如:用户鼠标点击触发的更新。
31  if (rootsWithPendingDiscreteUpdates !== null) {
32    if (
33      !hasDiscreteLanes(remainingLanes) &&
34      rootsWithPendingDiscreteUpdates.has(root)
35    ) {
36      rootsWithPendingDiscreteUpdates.delete(root);
37    }
38  }
39
40  // 重置全局变量
41  if (root === workInProgressRoot) {
42    workInProgressRoot = null;
43    workInProgress = null;
44    workInProgressRootRenderLanes = NoLanes;
45  } else {
46  }
47
48  // 将effectList赋值给firstEffect
49  // 由于每个fiber的effectList只包含他的子孙节点 TODO
50  // 所以根节点如果有effectTag则不会被包含进来
51  // 所以这里将有effectTag的根节点插入到effectList尾部
52  // 这样才能保证有effect的fiber都在effectList中
53  let firstEffect;
54  // 根节点有effect的情况
55  if (finishedWork.effectTag > PerformedWork) {
56    if (finishedWork.lastEffect !== null) {
57        //如果已经存在lastEffect,则把根节点放在effect链的最后
58      finishedWork.lastEffect.nextEffect = finishedWork;
59      firstEffect = finishedWork.firstEffect;
60    } else {
61        // 如果没有lastEffect,则把根节点放在effect链的最前
62      firstEffect = finishedWork;
63    }
64  } else {
65    // 根节点没有effectTag
66    firstEffect = finishedWork.firstEffect;
67  }
68
69
70  // layout阶段之后
71  /**
72   * 主要
73   */
74
75  const rootDidHavePassiveEffects = rootDoseHavePassiveEffects;
76
77  // useEffect相关
78  if(rootDoseHavePassiveEffects){
79    rootDoseHavePassiveEffects = false;
80    rootWithPendingPassiveEffects = root;
81    pendingPassiveEffectsLanes = lanes;
82    pendingPassiveEffectsRenderPriority = renderPriorityLevel;
83  } else {}
84
85  // 性能优化相关
86  if(remainingLanes !== NoLanes) {
87    if(enableSchedulerTracing){
88
89    }
90  } else {
91
92  }
93
94  if(enableSchedulerTracing){
95    if(!rootDidHavePassiveEffects){
96
97    }
98  }
99
100  // 检查无限循环的同步任务
101  if(ramainingLanes === SyncLane){
102
103  }
104
105  // 在离开commitRoot函数前调用, 触发一次新的调度,确保任何附加的任务被调度
106  ensureRootIsScheduled(root, now());
107
108  // ...处理未捕获错误及老版本遗留的边界问题
109
110  // 执行同步任务,这样同步任务不需要等到下次事件循环在执行
111  // 比如componentDidxxx中执行setState,useLayoutEffect中创建的更新会在这里被同步执行
112  flushSyncCallbackQueue();
113
114  return null;
115
116  // before mutation 阶段
117  // 重点在下面这个函数
118  function commitBeforeMutationEffects(finishedWork){
119    while (nextEffect !== null) {
120      const current = nextEffect.alternate;
121
122      if(!shouldFireAfterActiveInstanceBlur && focusedInstanceHandle !== null){
123        // focus blur相关
124      }
125
126      const effectTag = nextEffect.effectTag
127
128      // 调用getSnapshotBeforeUpdate
129      if((effectTag & Snapshot) !== NoEffect) {
130        commitBeforeMutationEffectsOnFiber(current, nextEffect);
131      }
132
133      // 调用useEffect
134      if((effectTag & Passive) !== NoEffect) {
135        if(!rootDoseHavePassiveEffects) {
136          rootDoseHavePassiveEffects = true;
137          // 由Schedule模块提供,用于以某个优先级异步调度一个回调函数
138          scheduleCallback(NormalSchedulerPriority, () => {
139            // 触发useEffect
140            flushPassiveEffects();
141            return null;
142          });
143        }
144      }
145      nextEffect = nextEffect.nextEffect
146    }
147  }
148
149  // mutation 阶段
150
151  nextEffect = firstEffect;
152  do{
153    try{
154      commitMutationEffects(root, renderPriorityLevel);
155    } catch(error) {
156      invariant(nextEffect !== null, 'Should be working on an effect.');
157      captureCommitPhaseError(nextEffect, error);
158      nextEffect = nextEffect.nextEffect;
159    }
160  } while (nextEffect !== null)
161
162  // mutation阶段核心
163  function commitMutationEffects(){
164    root: FiberRoot, 
165    renderPriorityLevel
166  } {
167    // 遍历effectList
168    while(nextEffect !== null) {
169      const effectTag = nextEffect.effectTag;
170
171      // 根据ContentReset effectTag重置文字节点
172      if(effectTag & ContentReset){
173        commitResetTextContent(nextEffect);
174      }
175
176      // 更新ref
177      if(effectTag & Ref){
178        const current = nextEffect.alternate;
179        if(current !== null){
180          commitDetachRef(current);
181        }
182      }
183
184      // 根据effectTag分别处理
185      const primaryEffectTag = effectTag & (Placement | Update | Deletion | Hydrating)
186
187      switch (primaryEffectTag) {
188        // 插入DOM
189        case Placement: {
190          commitPlacement(nextEffect);
191          nextEffect.effectTag &= ~Placement; // 记录执行后的状态
192          break;
193        }
194        // 插入DOM并更新DOM
195        case PlacementAndUpdate: {
196          commitPlacement(nextEffect);
197
198          nextEffect.effectTag &= ~Placement;
199
200          // 更新DOM
201          const current = nextEffect.alternate;
202          commitWork(current, nextEffect);
203          break;
204        }
205
206        // SSR相关操作 ...略
207
208        case Update: {
209          // 更新DOM
210          const current = nextEffect.alternate;
211          commitWork(current, nextEffect);
212          break;
213        }
214
215        case Deletion: {
216          // 删除DOM
217          commitDeletion(root, nextEffect, renderPriortyLevel);
218          break;
219        }
220      }
221
222      nextEffect = nextEffect.nextEffect;
223    }
224  }
225
226  function commitPlacement(nextEffect){
227    // 获取父级DOM节点。其中finishedWork为传入的Fiber节点
228    const parentFiber = getHostParentFiber(finishedWork);
229    // 父级DOM节点
230    const parentStateNode = parentFiber.stateNode;
231
232    // 获取Fiber节点的DOM兄弟节点
233    const before = getHostSibling(finishedWork);
234
235    // 根据兄弟节点是否存在决定调用 parentNode.insertBefore 或 parentNode.appendChild执行DOM插入操作
236    if(isContainer){
237      insertOrAppendPlacementNodeIntoContainer(finishedWork, before, parent);
238    } else {
239      insertOrAppendPlacementNode(finishedWork, before, parent)
240    }
241  }
242
243  function commitUpdate() {
244    // 根据Fiber.Tag分别处理
245
246    // tag为FunctionComponent的情况
247    // 该方法会遍历effectList 执行所有useLayoutEffect hook的销毁函数
248    commitHookEffectListUnmonut()
249
250    // tag为HostComponent的情况
251    for(let i = 0; i < updatePayload.length; i += 2){
252      const propKey = updatePayload[i];
253      const propValue = updatePayload[i + 1];
254
255      // 处理 style
256      if(propKey === STYLE){
257        setValueForStyles(domElement, propValue);
258      } else if (propKey === DANGEROUSLY_SET_INNER_HTML){
259        setInnerHtml(domElement, propValue);
260      } else if (propKey === CHILDREN) {
261        setTextContent(domElement, propValue)
262      } else {
263        // 处理剩余props
264        setValueForProperty(domElement, propKey, propValuem, isCustomComponentTag);
265      }
266    }
267  }
268
269  function commitDeletion() {
270    /**
271     * 递归调用ClassComponent的componentWillUnmount,从页面移除DOM
272     * 解绑ref
273     * 调度useEffect的销毁函数
274     */
275  }
276
277
278  // layout 阶段
279  /**
280   * 这个阶段是在DOM已经渲染完成后执行的,在这个阶段触发的生命周期和hook可以直接访问已改变的DOM
281   */
282
283   root.current = finishedWork;
284   nextEffect = firstEffect;
285   do{
286     try{
287       commitLayoutEffects(root, lanes);
288     } catch(error) {
289       invariant(nextEffect !== null, "Should be working on an effect.");
290       captureCommitPhaseError(nextEffect, error);
291       nextEffect = nextEffect.nextEffect;
292     }
293   } while (nextEffect !== null);
294
295   nextEffect = null;
296
297  function commitLayoutEffects(root: FiberRoot, committedLanes: Lanes){
298    while(nextEffect !== null){
299      const effectTag = nextEffect.effectTag;
300
301      // 调用生命周期钩子和hook
302      if(effectTag & (Update | Callback)){
303        const current = nextEffect.alternate;
304        commitLayoutEffectOnFiber(root, current, nextEffect, committedLanes);
305      }
306
307      // 赋值ref
308      if(effectTag & Ref) {
309        commitAttachRef(nextEffect);
310      }
311
312      nextEffect = nextEffect.nextEffect
313    }
314  }
315
316  function commitLayoutEffectOnFiber() {
317    // 根据fiber.tag对不同类型节点分别处理
318    // 对于ClassComponent, 会通过current === null 区分是mount还是update,调用componentDidMount 或componentDidUpdate
319    // 触发状态更新的this.setState如果赋值了第二个参数回调函数,也会在此时调用。
320
321    // 对于FunctionCompnent及相关类型
322    switch (finishedWork.tag) {
323      // 以下都是FunctionComponent及相关类型
324      case FunctionComponent:
325      case ForwardRef:
326      case SimpleMemoComponent:
327      case Block: {
328        // 执行useLayoutEffect的回调函数
329        commmitHookEffectListMount(HookLayout | HookHasEffect, finishedWork);
330        // 调度useEffect的销毁函数与回调函数
331        schedulePassiveEffects(finishedWork);
332        return;
333      }
334    }
335  }
336
337  // 深入flushPassiveEffects (中文意思为:清洗被动效果)
338  // flushPassiveEffects内部会设置优先级,并执行flushPassiveEffectsImpl,Impl的意思是实现,即implementation的简写
339  /**
340   * flushPassiveEffectsImpl主要做三件事
341   * 1. 调用该useEffect在上一次render时的销毁函数
342   * 2. 调用该useEffect在本次render时的回调函数
343   * 3. 如果存在同步任务,不需要等待下次事件循环的宏任务,提前执行
344   * 1,2两步会在layout阶段之后异步执行
345   */
346
347// 阶段一:销毁函数的执行
348/**
349 * useEffect会保证在执行完全部的销毁函数之后,再执行回调函数
350 */
351
352// pendingPassiveHookEffectsUnmount中保存了所有需要执行销毁的useEffect
353const unmountEffects = pendingPassiveHookEffectsUnmount;
354pendingPassiveHookEffectsUnmount = [];
355for (let i = 0; i < unmountEffects.length; i += 2) {
356  const effect = ((unmountEffects[i]: any): HookEffect);
357  const fiber = ((unmountEffects[i + 1]: any): Fiber);
358  const distroy = effect.distroy;
359  effect.distroy = undefined;
360
361  if(typeof destroy == 'function') {
362    // 销毁函数存在则执行
363    try {
364      destroy();
365    } catch (error) {
366      captureCommitPhaseError(fiber, error);
367    }
368  }
369}
370
371
372function schedulePassiveEffects(finishedWork: Fiber) {
373  const updateQueue: FunctionComponentUpdateQueue | null = (finishedWork.updateQueue: any);
374  const lastEffect = updateQueue !== null ? updateQueue.lastEffect : null;
375
376  if(lastEffect !== null) {
377    const firstEffect = lastEffect.next;
378    let effect = firstEffect;
379
380    do {
381      const { next, tag } = effect;
382      if(
383        (tag & HookPassive) !== NoHookEffect &&
384        (tag & HookHasEffect) !== NoHookEffect
385       ) {
386         // 向pendingPassiveHookEffectsUnmount数组内push要销毁的effect
387         enqueuePendingPassiveHookEffectUnmount(finishedWork, effect);
388         // 向pendingPassiveHookEffectMount数组内push要执行回调的effect
389         enqueuePendingPassiveHookEffectMount(finishedWork, effect);
390       }
391       effect = next;
392    } while ( effect !== firstEffect );
393  }
394}
395
396// 阶段二: 回调函数的执行
397const mountEffects = pendingPassiveHookEffectsMount;
398pendingPassiveHookEffectsMount = [];
399for(let i = 0; i < mountEffects.length; i += 2) {
400  const effect = ((mountEffects[i]: any): HookEffect);
401  const fiber = ((mountEffects[i + 1]: any): Fiber);
402
403  try {
404    const create = effect.create;
405    effect.distroy = create();
406  } catch (error) {
407    captureCommitPhaseError(fiber, error);
408  }
409}
Full Screen

ReactFiberCommitWork.new.js

Source: ReactFiberCommitWork.new.js Github

copy
1/**
2 * Copyright (c) Facebook, Inc. and its affiliates.
3 *
4 * This source code is licensed under the MIT license found in the
5 * LICENSE file in the root directory of this source tree.
6 *
7 * @flow
8 */
9
10import type {
11  Instance,
12  TextInstance,
13  SuspenseInstance,
14  Container,
15  ChildSet,
16  UpdatePayload,
17} from './ReactFiberHostConfig';
18import type {Fiber} from './ReactInternalTypes';
19import type {FiberRoot} from './ReactInternalTypes';
20import type {SuspenseState} from './ReactFiberSuspenseComponent.new';
21import type {UpdateQueue} from './ReactUpdateQueue.new';
22import type {FunctionComponentUpdateQueue} from './ReactFiberHooks.new';
23import type {Wakeable} from 'shared/ReactTypes';
24import type {ReactPriorityLevel} from './ReactInternalTypes';
25import type {OffscreenState} from './ReactFiberOffscreenComponent';
26import type {HookFlags} from './ReactHookEffectTags';
27import type {Flags} from './ReactFiberFlags';
28
29import {unstable_wrap as Schedule_tracing_wrap} from 'scheduler/tracing';
30import {
31  enableSchedulerTracing,
32  enableProfilerTimer,
33  enableProfilerCommitHooks,
34  enableSuspenseServerRenderer,
35  enableFundamentalAPI,
36  enableSuspenseCallback,
37  enableScopeAPI,
38  enableDoubleInvokingEffects,
39  enableRecursiveCommitTraversal,
40} from 'shared/ReactFeatureFlags';
41import {
42  FunctionComponent,
43  ForwardRef,
44  ClassComponent,
45  HostRoot,
46  HostComponent,
47  HostText,
48  HostPortal,
49  Profiler,
50  SuspenseComponent,
51  DehydratedFragment,
52  IncompleteClassComponent,
53  MemoComponent,
54  SimpleMemoComponent,
55  SuspenseListComponent,
56  FundamentalComponent,
57  ScopeComponent,
58  OffscreenComponent,
59  LegacyHiddenComponent,
60} from './ReactWorkTags';
61import {
62  invokeGuardedCallback,
63  hasCaughtError,
64  clearCaughtError,
65} from 'shared/ReactErrorUtils';
66import {
67  NoFlags,
68  ContentReset,
69  Placement,
70  Snapshot,
71  Visibility,
72  Update,
73  Callback,
74  Ref,
75  PlacementAndUpdate,
76  Hydrating,
77  HydratingAndUpdate,
78  Passive,
79  PassiveStatic,
80  BeforeMutationMask,
81  MutationMask,
82  LayoutMask,
83  PassiveMask,
84  MountLayoutDev,
85  MountPassiveDev,
86} from './ReactFiberFlags';
87import getComponentName from 'shared/getComponentName';
88import invariant from 'shared/invariant';
89import {
90  current as currentDebugFiberInDEV,
91  resetCurrentFiber as resetCurrentDebugFiberInDEV,
92  setCurrentFiber as setCurrentDebugFiberInDEV,
93} from './ReactCurrentFiber';
94import {onCommitUnmount} from './ReactFiberDevToolsHook.new';
95import {resolveDefaultProps} from './ReactFiberLazyComponent.new';
96import {
97  getCommitTime,
98  recordLayoutEffectDuration,
99  startLayoutEffectTimer,
100  recordPassiveEffectDuration,
101  startPassiveEffectTimer,
102} from './ReactProfilerTimer.new';
103import {
104  NoMode,
105  BlockingMode,
106  ConcurrentMode,
107  ProfileMode,
108} from './ReactTypeOfMode';
109import {commitUpdateQueue} from './ReactUpdateQueue.new';
110import {
111  getPublicInstance,
112  supportsMutation,
113  supportsPersistence,
114  supportsHydration,
115  prepareForCommit,
116  beforeActiveInstanceBlur,
117  commitMount,
118  commitUpdate,
119  resetTextContent,
120  commitTextUpdate,
121  appendChild,
122  appendChildToContainer,
123  insertBefore,
124  insertInContainerBefore,
125  removeChild,
126  removeChildFromContainer,
127  clearSuspenseBoundary,
128  clearSuspenseBoundaryFromContainer,
129  replaceContainerChildren,
130  createContainerChildSet,
131  hideInstance,
132  hideTextInstance,
133  unhideInstance,
134  unhideTextInstance,
135  unmountFundamentalComponent,
136  updateFundamentalComponent,
137  commitHydratedContainer,
138  commitHydratedSuspenseInstance,
139  clearContainer,
140  prepareScopeUpdate,
141} from './ReactFiberHostConfig';
142import {
143  captureCommitPhaseError,
144  resolveRetryWakeable,
145  markCommitTimeOfFallback,
146} from './ReactFiberWorkLoop.new';
147import {
148  NoFlags as NoHookEffect,
149  HasEffect as HookHasEffect,
150  Layout as HookLayout,
151  Passive as HookPassive,
152} from './ReactHookEffectTags';
153import {didWarnAboutReassigningProps} from './ReactFiberBeginWork.new';
154import {doesFiberContain} from './ReactFiberTreeReflection';
155
156let nextEffect: Fiber | null = null;
157
158// Used to avoid traversing the return path to find the nearest Profiler ancestor during commit.
159let nearestProfilerOnStack: Fiber | null = null;
160
161let didWarnAboutUndefinedSnapshotBeforeUpdate: Set<mixed> | null = null;
162if (__DEV__) {
163  didWarnAboutUndefinedSnapshotBeforeUpdate = new Set();
164}
165
166const PossiblyWeakSet = typeof WeakSet === 'function' ? WeakSet : Set;
167
168const callComponentWillUnmountWithTimer = function(current, instance) {
169  instance.props = current.memoizedProps;
170  instance.state = current.memoizedState;
171  if (
172    enableProfilerTimer &&
173    enableProfilerCommitHooks &&
174    current.mode & ProfileMode
175  ) {
176    try {
177      startLayoutEffectTimer();
178      instance.componentWillUnmount();
179    } finally {
180      recordLayoutEffectDuration(current);
181    }
182  } else {
183    instance.componentWillUnmount();
184  }
185};
186
187// Capture errors so they don't interrupt unmounting.
188function safelyCallComponentWillUnmount(
189  current: Fiber,
190  instance: any,
191  nearestMountedAncestor: Fiber | null,
192) {
193  if (__DEV__) {
194    invokeGuardedCallback(
195      null,
196      callComponentWillUnmountWithTimer,
197      null,
198      current,
199      instance,
200    );
201    if (hasCaughtError()) {
202      const unmountError = clearCaughtError();
203      captureCommitPhaseError(current, nearestMountedAncestor, unmountError);
204    }
205  } else {
206    try {
207      callComponentWillUnmountWithTimer(current, instance);
208    } catch (unmountError) {
209      captureCommitPhaseError(current, nearestMountedAncestor, unmountError);
210    }
211  }
212}
213
214/** @noinline */
215function safelyDetachRef(current: Fiber, nearestMountedAncestor: Fiber) {
216  const ref = current.ref;
217  if (ref !== null) {
218    if (typeof ref === 'function') {
219      if (__DEV__) {
220        if (
221          enableProfilerTimer &&
222          enableProfilerCommitHooks &&
223          current.mode & ProfileMode
224        ) {
225          startLayoutEffectTimer();
226          invokeGuardedCallback(null, ref, null, null);
227          recordLayoutEffectDuration(current);
228        } else {
229          invokeGuardedCallback(null, ref, null, null);
230        }
231
232        if (hasCaughtError()) {
233          const refError = clearCaughtError();
234          captureCommitPhaseError(current, nearestMountedAncestor, refError);
235        }
236      } else {
237        try {
238          if (
239            enableProfilerTimer &&
240            enableProfilerCommitHooks &&
241            current.mode & ProfileMode
242          ) {
243            try {
244              startLayoutEffectTimer();
245              ref(null);
246            } finally {
247              recordLayoutEffectDuration(current);
248            }
249          } else {
250            ref(null);
251          }
252        } catch (refError) {
253          captureCommitPhaseError(current, nearestMountedAncestor, refError);
254        }
255      }
256    } else {
257      ref.current = null;
258    }
259  }
260}
261
262export function safelyCallDestroy(
263  current: Fiber,
264  nearestMountedAncestor: Fiber | null,
265  destroy: () => void,
266) {
267  if (__DEV__) {
268    invokeGuardedCallback(null, destroy, null);
269    if (hasCaughtError()) {
270      const error = clearCaughtError();
271      captureCommitPhaseError(current, nearestMountedAncestor, error);
272    }
273  } else {
274    try {
275      destroy();
276    } catch (error) {
277      captureCommitPhaseError(current, nearestMountedAncestor, error);
278    }
279  }
280}
281
282/** @noinline */
283function commitHookEffectListUnmount(
284  flags: HookFlags,
285  finishedWork: Fiber,
286  nearestMountedAncestor: Fiber | null,
287) {
288  const updateQueue: FunctionComponentUpdateQueue | null = (finishedWork.updateQueue: any);
289  const lastEffect = updateQueue !== null ? updateQueue.lastEffect : null;
290  if (lastEffect !== null) {
291    const firstEffect = lastEffect.next;
292    let effect = firstEffect;
293    do {
294      if ((effect.tag & flags) === flags) {
295        // Unmount
296        const destroy = effect.destroy;
297        effect.destroy = undefined;
298        if (destroy !== undefined) {
299          safelyCallDestroy(finishedWork, nearestMountedAncestor, destroy);
300        }
301      }
302      effect = effect.next;
303    } while (effect !== firstEffect);
304  }
305}
306
307/** @noinline */
308function commitHookEffectListMount(flags: HookFlags, finishedWork: Fiber) {
309  const updateQueue: FunctionComponentUpdateQueue | null = (finishedWork.updateQueue: any);
310  const lastEffect = updateQueue !== null ? updateQueue.lastEffect : null;
311  if (lastEffect !== null) {
312    const firstEffect = lastEffect.next;
313    let effect = firstEffect;
314    do {
315      if ((effect.tag & flags) === flags) {
316        // Mount
317        const create = effect.create;
318        effect.destroy = create();
319
320        if (__DEV__) {
321          const destroy = effect.destroy;
322          if (destroy !== undefined && typeof destroy !== 'function') {
323            let addendum;
324            if (destroy === null) {
325              addendum =
326                ' You returned null. If your effect does not require clean ' +
327                'up, return undefined (or nothing).';
328            } else if (typeof destroy.then === 'function') {
329              addendum =
330                '\n\nIt looks like you wrote useEffect(async () => ...) or returned a Promise. ' +
331                'Instead, write the async function inside your effect ' +
332                'and call it immediately:\n\n' +
333                'useEffect(() => {\n' +
334                '  async function fetchData() {\n' +
335                '    // You can await here\n' +
336                '    const response = await MyAPI.getData(someId);\n' +
337                '    // ...\n' +
338                '  }\n' +
339                '  fetchData();\n' +
340                `}, [someId]); // Or [] if effect doesn't need props or state\n\n` +
341                'Learn more about data fetching with Hooks: https://reactjs.org/link/hooks-data-fetching';
342            } else {
343              addendum = ' You returned: ' + destroy;
344            }
345            console.error(
346              'An effect function must not return anything besides a function, ' +
347                'which is used for clean-up.%s',
348              addendum,
349            );
350          }
351        }
352      }
353      effect = effect.next;
354    } while (effect !== firstEffect);
355  }
356}
357
358function commitProfilerPassiveEffect(
359  finishedRoot: FiberRoot,
360  finishedWork: Fiber,
361): void {
362  if (enableProfilerTimer && enableProfilerCommitHooks) {
363    switch (finishedWork.tag) {
364      case Profiler: {
365        const {passiveEffectDuration} = finishedWork.stateNode;
366        const {id, onPostCommit} = finishedWork.memoizedProps;
367
368        // This value will still reflect the previous commit phase.
369        // It does not get reset until the start of the next commit phase.
370        const commitTime = getCommitTime();
371
372        if (typeof onPostCommit === 'function') {
373          if (enableSchedulerTracing) {
374            onPostCommit(
375              id,
376              finishedWork.alternate === null ? 'mount' : 'update',
377              passiveEffectDuration,
378              commitTime,
379              finishedRoot.memoizedInteractions,
380            );
381          } else {
382            onPostCommit(
383              id,
384              finishedWork.alternate === null ? 'mount' : 'update',
385              passiveEffectDuration,
386              commitTime,
387            );
388          }
389        }
390        break;
391      }
392      default:
393        break;
394    }
395  }
396}
397
398let focusedInstanceHandle: null | Fiber = null;
399let shouldFireAfterActiveInstanceBlur: boolean = false;
400
401export function commitBeforeMutationEffects(
402  root: FiberRoot,
403  firstChild: Fiber,
404) {
405  focusedInstanceHandle = prepareForCommit(root.containerInfo);
406
407  if (enableRecursiveCommitTraversal) {
408    recursivelyCommitBeforeMutationEffects(firstChild);
409  } else {
410    nextEffect = firstChild;
411    iterativelyCommitBeforeMutationEffects_begin();
412  }
413
414  // We no longer need to track the active instance fiber
415  const shouldFire = shouldFireAfterActiveInstanceBlur;
416  shouldFireAfterActiveInstanceBlur = false;
417  focusedInstanceHandle = null;
418
419  return shouldFire;
420}
421
422function recursivelyCommitBeforeMutationEffects(firstChild: Fiber) {
423  let fiber = firstChild;
424  while (fiber !== null) {
425    // TODO: Should wrap this in flags check, too, as optimization
426    if (fiber.deletions !== null) {
427      commitBeforeMutationEffectsDeletions(fiber.deletions);
428    }
429
430    const child = fiber.child;
431    if (fiber.subtreeFlags & BeforeMutationMask && child !== null) {
432      recursivelyCommitBeforeMutationEffects(child);
433    }
434
435    if (__DEV__) {
436      setCurrentDebugFiberInDEV(fiber);
437      invokeGuardedCallback(
438        null,
439        commitBeforeMutationEffectsOnFiber,
440        null,
441        fiber,
442      );
443      if (hasCaughtError()) {
444        const error = clearCaughtError();
445        captureCommitPhaseError(fiber, fiber.return, error);
446      }
447      resetCurrentDebugFiberInDEV();
448    } else {
449      try {
450        commitBeforeMutationEffectsOnFiber(fiber);
451      } catch (error) {
452        captureCommitPhaseError(fiber, fiber.return, error);
453      }
454    }
455    fiber = fiber.sibling;
456  }
457}
458
459function iterativelyCommitBeforeMutationEffects_begin() {
460  while (nextEffect !== null) {
461    const fiber = nextEffect;
462
463    // TODO: Should wrap this in flags check, too, as optimization
464    const deletions = fiber.deletions;
465    if (deletions !== null) {
466      commitBeforeMutationEffectsDeletions(deletions);
467    }
468
469    const child = fiber.child;
470    if (
471      (fiber.subtreeFlags & BeforeMutationMask) !== NoFlags &&
472      child !== null
473    ) {
474      child.return = fiber;
475      nextEffect = child;
476    } else {
477      iterativelyCommitBeforeMutationEffects_complete();
478    }
479  }
480}
481
482function iterativelyCommitBeforeMutationEffects_complete() {
483  while (nextEffect !== null) {
484    const fiber = nextEffect;
485    if (__DEV__) {
486      setCurrentDebugFiberInDEV(fiber);
487      invokeGuardedCallback(
488        null,
489        commitBeforeMutationEffectsOnFiber,
490        null,
491        fiber,
492      );
493      if (hasCaughtError()) {
494        const error = clearCaughtError();
495        captureCommitPhaseError(fiber, fiber.return, error);
496      }
497      resetCurrentDebugFiberInDEV();
498    } else {
499      try {
500        commitBeforeMutationEffectsOnFiber(fiber);
501      } catch (error) {
502        captureCommitPhaseError(fiber, fiber.return, error);
503      }
504    }
505
506    const sibling = fiber.sibling;
507    if (sibling !== null) {
508      sibling.return = fiber.return;
509      nextEffect = sibling;
510      return;
511    }
512
513    nextEffect = fiber.return;
514  }
515}
516
517/** @noinline */
518function commitBeforeMutationEffectsOnFiber(finishedWork: Fiber) {
519  const current = finishedWork.alternate;
520  const flags = finishedWork.flags;
521
522  if (!shouldFireAfterActiveInstanceBlur && focusedInstanceHandle !== null) {
523    // Check to see if the focused element was inside of a hidden (Suspense) subtree.
524    if (
525      // TODO: Can optimize this further with separate Hide and Show flags. We
526      // only care about Hide here.
527      (flags & Visibility) !== NoFlags &&
528      finishedWork.tag === SuspenseComponent &&
529      isSuspenseBoundaryBeingHidden(current, finishedWork) &&
530      doesFiberContain(finishedWork, focusedInstanceHandle)
531    ) {
532      shouldFireAfterActiveInstanceBlur = true;
533      beforeActiveInstanceBlur(finishedWork);
534    }
535  }
536
537  if ((flags & Snapshot) !== NoFlags) {
538    setCurrentDebugFiberInDEV(finishedWork);
539    switch (finishedWork.tag) {
540      case FunctionComponent:
541      case ForwardRef:
542      case SimpleMemoComponent: {
543        break;
544      }
545      case ClassComponent: {
546        if (finishedWork.flags & Snapshot) {
547          if (current !== null) {
548            const prevProps = current.memoizedProps;
549            const prevState = current.memoizedState;
550            const instance = finishedWork.stateNode;
551            // We could update instance props and state here,
552            // but instead we rely on them being set during last render.
553            // TODO: revisit this when we implement resuming.
554            if (__DEV__) {
555              if (
556                finishedWork.type === finishedWork.elementType &&
557                !didWarnAboutReassigningProps
558              ) {
559                if (instance.props !== finishedWork.memoizedProps) {
560                  console.error(
561                    'Expected %s props to match memoized props before ' +
562                      'getSnapshotBeforeUpdate. ' +
563                      'This might either be because of a bug in React, or because ' +
564                      'a component reassigns its own `this.props`. ' +
565                      'Please file an issue.',
566                    getComponentName(finishedWork.type) || 'instance',
567                  );
568                }
569                if (instance.state !== finishedWork.memoizedState) {
570                  console.error(
571                    'Expected %s state to match memoized state before ' +
572                      'getSnapshotBeforeUpdate. ' +
573                      'This might either be because of a bug in React, or because ' +
574                      'a component reassigns its own `this.state`. ' +
575                      'Please file an issue.',
576                    getComponentName(finishedWork.type) || 'instance',
577                  );
578                }
579              }
580            }
581            const snapshot = instance.getSnapshotBeforeUpdate(
582              finishedWork.elementType === finishedWork.type
583                ? prevProps
584                : resolveDefaultProps(finishedWork.type, prevProps),
585              prevState,
586            );
587            if (__DEV__) {
588              const didWarnSet = ((didWarnAboutUndefinedSnapshotBeforeUpdate: any): Set<mixed>);
589              if (
590                snapshot === undefined &&
591                !didWarnSet.has(finishedWork.type)
592              ) {
593                didWarnSet.add(finishedWork.type);
594                console.error(
595                  '%s.getSnapshotBeforeUpdate(): A snapshot value (or null) ' +
596                    'must be returned. You have returned undefined.',
597                  getComponentName(finishedWork.type),
598                );
599              }
600            }
601            instance.__reactInternalSnapshotBeforeUpdate = snapshot;
602          }
603        }
604        break;
605      }
606      case HostRoot: {
607        if (supportsMutation) {
608          if (finishedWork.flags & Snapshot) {
609            const root = finishedWork.stateNode;
610            clearContainer(root.containerInfo);
611          }
612        }
613        break;
614      }
615      case HostComponent:
616      case HostText:
617      case HostPortal:
618      case IncompleteClassComponent:
619        // Nothing to do for these component types
620        break;
621      default:
622        invariant(
623          false,
624          'This unit of work tag should not have side-effects. This error is ' +
625            'likely caused by a bug in React. Please file an issue.',
626        );
627    }
628    resetCurrentDebugFiberInDEV();
629  }
630}
631
632/** @noinline */
633function commitBeforeMutationEffectsDeletions(deletions: Array<Fiber>) {
634  for (let i = 0; i < deletions.length; i++) {
635    const fiber = deletions[i];
636
637    // TODO (effects) It would be nice to avoid calling doesFiberContain()
638    // Maybe we can repurpose one of the subtreeFlags positions for this instead?
639    // Use it to store which part of the tree the focused instance is in?
640    // This assumes we can safely determine that instance during the "render" phase.
641
642    if (doesFiberContain(fiber, ((focusedInstanceHandle: any): Fiber))) {
643      shouldFireAfterActiveInstanceBlur = true;
644      beforeActiveInstanceBlur(fiber);
645    }
646  }
647}
648
649export function commitMutationEffects(
650  firstChild: Fiber,
651  root: FiberRoot,
652  renderPriorityLevel: ReactPriorityLevel,
653) {
654  if (enableRecursiveCommitTraversal) {
655    recursivelyCommitMutationEffects(firstChild, root, renderPriorityLevel);
656  } else {
657    nextEffect = firstChild;
658    iterativelyCommitMutationEffects_begin(root, renderPriorityLevel);
659  }
660}
661
662function recursivelyCommitMutationEffects(
663  firstChild: Fiber,
664  root: FiberRoot,
665  renderPriorityLevel: ReactPriorityLevel,
666) {
667  let fiber = firstChild;
668  while (fiber !== null) {
669    const deletions = fiber.deletions;
670    if (deletions !== null) {
671      commitMutationEffectsDeletions(
672        deletions,
673        fiber,
674        root,
675        renderPriorityLevel,
676      );
677    }
678
679    if (fiber.child !== null) {
680      const mutationFlags = fiber.subtreeFlags & MutationMask;
681      if (mutationFlags !== NoFlags) {
682        recursivelyCommitMutationEffects(
683          fiber.child,
684          root,
685          renderPriorityLevel,
686        );
687      }
688    }
689
690    if (__DEV__) {
691      setCurrentDebugFiberInDEV(fiber);
692      invokeGuardedCallback(
693        null,
694        commitMutationEffectsOnFiber,
695        null,
696        fiber,
697        root,
698        renderPriorityLevel,
699      );
700      if (hasCaughtError()) {
701        const error = clearCaughtError();
702        captureCommitPhaseError(fiber, fiber.return, error);
703      }
704      resetCurrentDebugFiberInDEV();
705    } else {
706      try {
707        commitMutationEffectsOnFiber(fiber, root, renderPriorityLevel);
708      } catch (error) {
709        captureCommitPhaseError(fiber, fiber.return, error);
710      }
711    }
712    fiber = fiber.sibling;
713  }
714}
715
716function iterativelyCommitMutationEffects_begin(
717  root: FiberRoot,
718  renderPriorityLevel: ReactPriorityLevel,
719) {
720  while (nextEffect !== null) {
721    const fiber = nextEffect;
722
723    // TODO: Should wrap this in flags check, too, as optimization
724    const deletions = fiber.deletions;
725    if (deletions !== null) {
726      commitMutationEffectsDeletions(
727        deletions,
728        fiber,
729        root,
730        renderPriorityLevel,
731      );
732    }
733
734    const child = fiber.child;
735    if ((fiber.subtreeFlags & MutationMask) !== NoFlags && child !== null) {
736      child.return = fiber;
737      nextEffect = child;
738    } else {
739      iterativelyCommitMutationEffects_complete(root, renderPriorityLevel);
740    }
741  }
742}
743
744function iterativelyCommitMutationEffects_complete(
745  root: FiberRoot,
746  renderPriorityLevel: ReactPriorityLevel,
747) {
748  while (nextEffect !== null) {
749    const fiber = nextEffect;
750    if (__DEV__) {
751      setCurrentDebugFiberInDEV(fiber);
752      invokeGuardedCallback(
753        null,
754        commitMutationEffectsOnFiber,
755        null,
756        fiber,
757        root,
758        renderPriorityLevel,
759      );
760      if (hasCaughtError()) {
761        const error = clearCaughtError();
762        captureCommitPhaseError(fiber, fiber.return, error);
763      }
764      resetCurrentDebugFiberInDEV();
765    } else {
766      try {
767        commitMutationEffectsOnFiber(fiber, root, renderPriorityLevel);
768      } catch (error) {
769        captureCommitPhaseError(fiber, fiber.return, error);
770      }
771    }
772
773    const sibling = fiber.sibling;
774    if (sibling !== null) {
775      sibling.return = fiber.return;
776      nextEffect = sibling;
777      return;
778    }
779
780    nextEffect = fiber.return;
781  }
782}
783
784/** @noinline */
785function commitMutationEffectsOnFiber(
786  fiber: Fiber,
787  root: FiberRoot,
788  renderPriorityLevel,
789) {
790  const flags = fiber.flags;
791  if (flags & ContentReset) {
792    commitResetTextContent(fiber);
793  }
794
795  if (flags & Ref) {
796    const current = fiber.alternate;
797    if (current !== null) {
798      commitDetachRef(current);
799    }
800    if (enableScopeAPI) {
801      // TODO: This is a temporary solution that allowed us to transition away from React Flare on www.
802      if (fiber.tag === ScopeComponent) {
803        commitAttachRef(fiber);
804      }
805    }
806  }
807
808  // The following switch statement is only concerned about placement,
809  // updates, and deletions. To avoid needing to add a case for every possible
810  // bitmap value, we remove the secondary effects from the effect tag and
811  // switch on that value.
812  const primaryFlags = flags & (Placement | Update | Hydrating);
813  switch (primaryFlags) {
814    case Placement: {
815      commitPlacement(fiber);
816      // Clear the "placement" from effect tag so that we know that this is
817      // inserted, before any life-cycles like componentDidMount gets called.
818      // TODO: findDOMNode doesn't rely on this any more but isMounted does
819      // and isMounted is deprecated anyway so we should be able to kill this.
820      fiber.flags &= ~Placement;
821      break;
822    }
823    case PlacementAndUpdate: {
824      // Placement
825      commitPlacement(fiber);
826      // Clear the "placement" from effect tag so that we know that this is
827      // inserted, before any life-cycles like componentDidMount gets called.
828      fiber.flags &= ~Placement;
829
830      // Update
831      const current = fiber.alternate;
832      commitWork(current, fiber);
833      break;
834    }
835    case Hydrating: {
836      fiber.flags &= ~Hydrating;
837      break;
838    }
839    case HydratingAndUpdate: {
840      fiber.flags &= ~Hydrating;
841
842      // Update
843      const current = fiber.alternate;
844      commitWork(current, fiber);
845      break;
846    }
847    case Update: {
848      const current = fiber.alternate;
849      commitWork(current, fiber);
850      break;
851    }
852  }
853}
854
855/** @noinline */
856function commitMutationEffectsDeletions(
857  deletions: Array<Fiber>,
858  nearestMountedAncestor: Fiber,
859  root: FiberRoot,
860  renderPriorityLevel,
861) {
862  for (let i = 0; i < deletions.length; i++) {
863    const childToDelete = deletions[i];
864    if (__DEV__) {
865      invokeGuardedCallback(
866        null,
867        commitDeletion,
868        null,
869        root,
870        childToDelete,
871        nearestMountedAncestor,
872        renderPriorityLevel,
873      );
874      if (hasCaughtError()) {
875        const error = clearCaughtError();
876        captureCommitPhaseError(childToDelete, nearestMountedAncestor, error);
877      }
878    } else {
879      try {
880        commitDeletion(
881          root,
882          childToDelete,
883          nearestMountedAncestor,
884          renderPriorityLevel,
885        );
886      } catch (error) {
887        captureCommitPhaseError(childToDelete, nearestMountedAncestor, error);
888      }
889    }
890  }
891}
892
893export function commitLayoutEffects(
894  finishedWork: Fiber,
895  finishedRoot: FiberRoot,
896) {
897  if (enableRecursiveCommitTraversal) {
898    if (__DEV__) {
899      setCurrentDebugFiberInDEV(finishedWork);
900      invokeGuardedCallback(
901        null,
902        recursivelyCommitLayoutEffects,
903        null,
904        finishedWork,
905        finishedRoot,
906      );
907      if (hasCaughtError()) {
908        const error = clearCaughtError();
909        captureCommitPhaseError(finishedWork, null, error);
910      }
911      resetCurrentDebugFiberInDEV();
912    } else {
913      try {
914        recursivelyCommitLayoutEffects(finishedWork, finishedRoot);
915      } catch (error) {
916        captureCommitPhaseError(finishedWork, null, error);
917      }
918    }
919  } else {
920    nextEffect = finishedWork;
921    iterativelyCommitLayoutEffects_begin(finishedWork, finishedRoot);
922  }
923}
924
925function recursivelyCommitLayoutEffects(
926  finishedWork: Fiber,
927  finishedRoot: FiberRoot,
928) {
929  const {flags, tag} = finishedWork;
930  switch (tag) {
931    case Profiler: {
932      let prevProfilerOnStack = null;
933      if (enableProfilerTimer && enableProfilerCommitHooks) {
934        prevProfilerOnStack = nearestProfilerOnStack;
935        nearestProfilerOnStack = finishedWork;
936      }
937
938      let child = finishedWork.child;
939      while (child !== null) {
940        const primarySubtreeFlags = finishedWork.subtreeFlags & LayoutMask;
941        if (primarySubtreeFlags !== NoFlags) {
942          if (__DEV__) {
943            const prevCurrentFiberInDEV = currentDebugFiberInDEV;
944            setCurrentDebugFiberInDEV(child);
945            invokeGuardedCallback(
946              null,
947              recursivelyCommitLayoutEffects,
948              null,
949              child,
950              finishedRoot,
951            );
952            if (hasCaughtError()) {
953              const error = clearCaughtError();
954              captureCommitPhaseError(child, finishedWork, error);
955            }
956            if (prevCurrentFiberInDEV !== null) {
957              setCurrentDebugFiberInDEV(prevCurrentFiberInDEV);
958            } else {
959              resetCurrentDebugFiberInDEV();
960            }
961          } else {
962            try {
963              recursivelyCommitLayoutEffects(child, finishedRoot);
964            } catch (error) {
965              captureCommitPhaseError(child, finishedWork, error);
966            }
967          }
968        }
969        child = child.sibling;
970      }
971
972      const primaryFlags = flags & (Update | Callback);
973      if (primaryFlags !== NoFlags) {
974        if (enableProfilerTimer) {
975          if (__DEV__) {
976            const prevCurrentFiberInDEV = currentDebugFiberInDEV;
977            setCurrentDebugFiberInDEV(finishedWork);
978            invokeGuardedCallback(
979              null,
980              commitLayoutEffectsForProfiler,
981              null,
982              finishedWork,
983              finishedRoot,
984            );
985            if (hasCaughtError()) {
986              const error = clearCaughtError();
987              captureCommitPhaseError(finishedWork, finishedWork.return, error);
988            }
989            if (prevCurrentFiberInDEV !== null) {
990              setCurrentDebugFiberInDEV(prevCurrentFiberInDEV);
991            } else {
992              resetCurrentDebugFiberInDEV();
993            }
994          } else {
995            try {
996              commitLayoutEffectsForProfiler(finishedWork, finishedRoot);
997            } catch (error) {
998              captureCommitPhaseError(finishedWork, finishedWork.return, error);
999            }
1000          }
1001        }
1002      }
1003
1004      if (enableProfilerTimer && enableProfilerCommitHooks) {
1005        // Propagate layout effect durations to the next nearest Profiler ancestor.
1006        // Do not reset these values until the next render so DevTools has a chance to read them first.
1007        if (prevProfilerOnStack !== null) {
1008          prevProfilerOnStack.stateNode.effectDuration +=
1009            finishedWork.stateNode.effectDuration;
1010        }
1011
1012        nearestProfilerOnStack = prevProfilerOnStack;
1013      }
1014      break;
1015    }
1016
1017    // case Offscreen: {
1018    //   TODO: Fast path to invoke all nested layout effects when Offscren goes from hidden to visible.
1019    //   break;
1020    // }
1021
1022    default: {
1023      let child = finishedWork.child;
1024      while (child !== null) {
1025        const primarySubtreeFlags = finishedWork.subtreeFlags & LayoutMask;
1026        if (primarySubtreeFlags !== NoFlags) {
1027          if (__DEV__) {
1028            const prevCurrentFiberInDEV = currentDebugFiberInDEV;
1029            setCurrentDebugFiberInDEV(child);
1030            invokeGuardedCallback(
1031              null,
1032              recursivelyCommitLayoutEffects,
1033              null,
1034              child,
1035              finishedRoot,
1036            );
1037            if (hasCaughtError()) {
1038              const error = clearCaughtError();
1039              captureCommitPhaseError(child, finishedWork, error);
1040            }
1041            if (prevCurrentFiberInDEV !== null) {
1042              setCurrentDebugFiberInDEV(prevCurrentFiberInDEV);
1043            } else {
1044              resetCurrentDebugFiberInDEV();
1045            }
1046          } else {
1047            try {
1048              recursivelyCommitLayoutEffects(child, finishedRoot);
1049            } catch (error) {
1050              captureCommitPhaseError(child, finishedWork, error);
1051            }
1052          }
1053        }
1054        child = child.sibling;
1055      }
1056
1057      const primaryFlags = flags & (Update | Callback);
1058      if (primaryFlags !== NoFlags) {
1059        switch (tag) {
1060          case FunctionComponent:
1061          case ForwardRef:
1062          case SimpleMemoComponent: {
1063            if (
1064              enableProfilerTimer &&
1065              enableProfilerCommitHooks &&
1066              finishedWork.mode & ProfileMode
1067            ) {
1068              try {
1069                startLayoutEffectTimer();
1070                commitHookEffectListMount(
1071                  HookLayout | HookHasEffect,
1072                  finishedWork,
1073                );
1074              } finally {
1075                recordLayoutEffectDuration(finishedWork);
1076              }
1077            } else {
1078              commitHookEffectListMount(
1079                HookLayout | HookHasEffect,
1080                finishedWork,
1081              );
1082            }
1083            break;
1084          }
1085          case ClassComponent: {
1086            // NOTE: Layout effect durations are measured within this function.
1087            commitLayoutEffectsForClassComponent(finishedWork);
1088            break;
1089          }
1090          case HostRoot: {
1091            commitLayoutEffectsForHostRoot(finishedWork);
1092            break;
1093          }
1094          case HostComponent: {
1095            commitLayoutEffectsForHostComponent(finishedWork);
1096            break;
1097          }
1098          case SuspenseComponent: {
1099            commitSuspenseHydrationCallbacks(finishedRoot, finishedWork);
1100            break;
1101          }
1102          case FundamentalComponent:
1103          case HostPortal:
1104          case HostText:
1105          case IncompleteClassComponent:
1106          case LegacyHiddenComponent:
1107          case OffscreenComponent:
1108          case ScopeComponent:
1109          case SuspenseListComponent: {
1110            // We have no life-cycles associated with these component types.
1111            break;
1112          }
1113          default: {
1114            invariant(
1115              false,
1116              'This unit of work tag should not have side-effects. This error is ' +
1117                'likely caused by a bug in React. Please file an issue.',
1118            );
1119          }
1120        }
1121      }
1122
1123      if (enableScopeAPI) {
1124        // TODO: This is a temporary solution that allowed us to transition away from React Flare on www.
1125        if (flags & Ref && tag !== ScopeComponent) {
1126          commitAttachRef(finishedWork);
1127        }
1128      } else {
1129        if (flags & Ref) {
1130          commitAttachRef(finishedWork);
1131        }
1132      }
1133      break;
1134    }
1135  }
1136}
1137
1138function iterativelyCommitLayoutEffects_begin(
1139  subtreeRoot: Fiber,
1140  finishedRoot: FiberRoot,
1141) {
1142  while (nextEffect !== null) {
1143    const finishedWork: Fiber = nextEffect;
1144    const firstChild = finishedWork.child;
1145
1146    if (
1147      (finishedWork.subtreeFlags & LayoutMask) !== NoFlags &&
1148      firstChild !== null
1149    ) {
1150      if (
1151        enableProfilerTimer &&
1152        enableProfilerCommitHooks &&
1153        finishedWork.tag === Profiler
1154      ) {
1155        const prevProfilerOnStack = nearestProfilerOnStack;
1156        nearestProfilerOnStack = finishedWork;
1157
1158        let child = firstChild;
1159        while (child !== null) {
1160          nextEffect = child;
1161          iterativelyCommitLayoutEffects_begin(child, finishedRoot);
1162          child = child.sibling;
1163        }
1164        nextEffect = finishedWork;
1165
1166        if ((finishedWork.flags & LayoutMask) !== NoFlags) {
1167          if (__DEV__) {
1168            setCurrentDebugFiberInDEV(finishedWork);
1169            invokeGuardedCallback(
1170              null,
1171              commitLayoutEffectsForProfiler,
1172              null,
1173              finishedWork,
1174              finishedRoot,
1175            );
1176            if (hasCaughtError()) {
1177              const error = clearCaughtError();
1178              captureCommitPhaseError(finishedWork, finishedWork.return, error);
1179            }
1180            resetCurrentDebugFiberInDEV();
1181          } else {
1182            try {
1183              commitLayoutEffectsForProfiler(finishedWork, finishedRoot);
1184            } catch (error) {
1185              captureCommitPhaseError(finishedWork, finishedWork.return, error);
1186            }
1187          }
1188        }
1189
1190        // Propagate layout effect durations to the next nearest Profiler ancestor.
1191        // Do not reset these values until the next render so DevTools has a chance to read them first.
1192        if (prevProfilerOnStack !== null) {
1193          prevProfilerOnStack.stateNode.effectDuration +=
1194            finishedWork.stateNode.effectDuration;
1195        }
1196        nearestProfilerOnStack = prevProfilerOnStack;
1197
1198        if (finishedWork === subtreeRoot) {
1199          nextEffect = null;
1200          return;
1201        }
1202        const sibling = finishedWork.sibling;
1203        if (sibling !== null) {
1204          sibling.return = finishedWork.return;
1205          nextEffect = sibling;
1206        } else {
1207          nextEffect = finishedWork.return;
1208          iterativelyCommitLayoutEffects_complete(subtreeRoot, finishedRoot);
1209        }
1210      } else {
1211        firstChild.return = finishedWork;
1212        nextEffect = firstChild;
1213      }
1214    } else {
1215      iterativelyCommitLayoutEffects_complete(subtreeRoot, finishedRoot);
1216    }
1217  }
1218}
1219
1220function iterativelyCommitLayoutEffects_complete(
1221  subtreeRoot: Fiber,
1222  finishedRoot: FiberRoot,
1223) {
1224  while (nextEffect !== null) {
1225    const fiber = nextEffect;
1226
1227    if ((fiber.flags & LayoutMask) !== NoFlags) {
1228      if (__DEV__) {
1229        setCurrentDebugFiberInDEV(fiber);
1230        invokeGuardedCallback(
1231          null,
1232          commitLayoutEffectsOnFiber,
1233          null,
1234          finishedRoot,
1235          fiber,
1236        );
1237        if (hasCaughtError()) {
1238          const error = clearCaughtError();
1239          captureCommitPhaseError(fiber, fiber.return, error);
1240        }
1241        resetCurrentDebugFiberInDEV();
1242      } else {
1243        try {
1244          commitLayoutEffectsOnFiber(finishedRoot, fiber);
1245        } catch (error) {
1246          captureCommitPhaseError(fiber, fiber.return, error);
1247        }
1248      }
1249    }
1250
1251    if (fiber === subtreeRoot) {
1252      nextEffect = null;
1253      return;
1254    }
1255
1256    const sibling = fiber.sibling;
1257    if (sibling !== null) {
1258      sibling.return = fiber.return;
1259      nextEffect = sibling;
1260      return;
1261    }
1262
1263    nextEffect = nextEffect.return;
1264  }
1265}
1266
1267function commitLayoutEffectsOnFiber(
1268  finishedRoot: FiberRoot,
1269  finishedWork: Fiber,
1270) {
1271  const tag = finishedWork.tag;
1272  const flags = finishedWork.flags;
1273  if ((flags & (Update | Callback)) !== NoFlags) {
1274    switch (tag) {
1275      case FunctionComponent:
1276      case ForwardRef:
1277      case SimpleMemoComponent: {
1278        if (
1279          enableProfilerTimer &&
1280          enableProfilerCommitHooks &&
1281          finishedWork.mode & ProfileMode
1282        ) {
1283          try {
1284            startLayoutEffectTimer();
1285            commitHookEffectListMount(HookLayout | HookHasEffect, finishedWork);
1286          } finally {
1287            recordLayoutEffectDuration(finishedWork);
1288          }
1289        } else {
1290          commitHookEffectListMount(HookLayout | HookHasEffect, finishedWork);
1291        }
1292        break;
1293      }
1294      case ClassComponent: {
1295        // NOTE: Layout effect durations are measured within this function.
1296        commitLayoutEffectsForClassComponent(finishedWork);
1297        break;
1298      }
1299      case HostRoot: {
1300        commitLayoutEffectsForHostRoot(finishedWork);
1301        break;
1302      }
1303      case HostComponent: {
1304        commitLayoutEffectsForHostComponent(finishedWork);
1305        break;
1306      }
1307      case Profiler: {
1308        commitLayoutEffectsForProfiler(finishedWork, finishedRoot);
1309        break;
1310      }
1311      case SuspenseComponent: {
1312        commitSuspenseHydrationCallbacks(finishedRoot, finishedWork);
1313        break;
1314      }
1315      case FundamentalComponent:
1316      case HostPortal:
1317      case HostText:
1318      case IncompleteClassComponent:
1319      case LegacyHiddenComponent:
1320      case OffscreenComponent:
1321      case ScopeComponent:
1322      case SuspenseListComponent: {
1323        // We have no life-cycles associated with these component types.
1324        break;
1325      }
1326      default: {
1327        invariant(
1328          false,
1329          'This unit of work tag should not have side-effects. This error is ' +
1330            'likely caused by a bug in React. Please file an issue.',
1331        );
1332      }
1333    }
1334  }
1335
1336  if (enableScopeAPI) {
1337    // TODO: This is a temporary solution that allowed us to transition away from React Flare on www.
1338    if (flags & Ref && tag !== ScopeComponent) {
1339      commitAttachRef(finishedWork);
1340    }
1341  } else {
1342    if (flags & Ref) {
1343      commitAttachRef(finishedWork);
1344    }
1345  }
1346}
1347
1348/** @noinline */
1349function commitLayoutEffectsForProfiler(
1350  finishedWork: Fiber,
1351  finishedRoot: FiberRoot,
1352) {
1353  if (enableProfilerTimer) {
1354    const flags = finishedWork.flags;
1355    const current = finishedWork.alternate;
1356
1357    const {onCommit, onRender} = finishedWork.memoizedProps;
1358    const {effectDuration} = finishedWork.stateNode;
1359
1360    const commitTime = getCommitTime();
1361
1362    const OnRenderFlag = Update;
1363    const OnCommitFlag = Callback;
1364
1365    if ((flags & OnRenderFlag) !== NoFlags && typeof onRender === 'function') {
1366      if (enableSchedulerTracing) {
1367        onRender(
1368          finishedWork.memoizedProps.id,
1369          current === null ? 'mount' : 'update',
1370          finishedWork.actualDuration,
1371          finishedWork.treeBaseDuration,
1372          finishedWork.actualStartTime,
1373          commitTime,
1374          finishedRoot.memoizedInteractions,
1375        );
1376      } else {
1377        onRender(
1378          finishedWork.memoizedProps.id,
1379          current === null ? 'mount' : 'update',
1380          finishedWork.actualDuration,
1381          finishedWork.treeBaseDuration,
1382          finishedWork.actualStartTime,
1383          commitTime,
1384        );
1385      }
1386    }
1387
1388    if (enableProfilerCommitHooks) {
1389      if (
1390        (flags & OnCommitFlag) !== NoFlags &&
1391        typeof onCommit === 'function'
1392      ) {
1393        if (enableSchedulerTracing) {
1394          onCommit(
1395            finishedWork.memoizedProps.id,
1396            current === null ? 'mount' : 'update',
1397            effectDuration,
1398            commitTime,
1399            finishedRoot.memoizedInteractions,
1400          );
1401        } else {
1402          onCommit(
1403            finishedWork.memoizedProps.id,
1404            current === null ? 'mount' : 'update',
1405            effectDuration,
1406            commitTime,
1407          );
1408        }
1409      }
1410    }
1411  }
1412}
1413
1414/** @noinline */
1415function commitLayoutEffectsForClassComponent(finishedWork: Fiber) {
1416  const instance = finishedWork.stateNode;
1417  const current = finishedWork.alternate;
1418  if (finishedWork.flags & Update) {
1419    if (current === null) {
1420      // We could update instance props and state here,
1421      // but instead we rely on them being set during last render.
1422      // TODO: revisit this when we implement resuming.
1423      if (__DEV__) {
1424        if (
1425          finishedWork.type === finishedWork.elementType &&
1426          !didWarnAboutReassigningProps
1427        ) {
1428          if (instance.props !== finishedWork.memoizedProps) {
1429            console.error(
1430              'Expected %s props to match memoized props before ' +
1431                'componentDidMount. ' +
1432                'This might either be because of a bug in React, or because ' +
1433                'a component reassigns its own `this.props`. ' +
1434                'Please file an issue.',
1435              getComponentName(finishedWork.type) || 'instance',
1436            );
1437          }
1438          if (instance.state !== finishedWork.memoizedState) {
1439            console.error(
1440              'Expected %s state to match memoized state before ' +
1441                'componentDidMount. ' +
1442                'This might either be because of a bug in React, or because ' +
1443                'a component reassigns its own `this.state`. ' +
1444                'Please file an issue.',
1445              getComponentName(finishedWork.type) || 'instance',
1446            );
1447          }
1448        }
1449      }
1450      if (
1451        enableProfilerTimer &&
1452        enableProfilerCommitHooks &&
1453        finishedWork.mode & ProfileMode
1454      ) {
1455        try {
1456          startLayoutEffectTimer();
1457          instance.componentDidMount();
1458        } finally {
1459          recordLayoutEffectDuration(finishedWork);
1460        }
1461      } else {
1462        instance.componentDidMount();
1463      }
1464    } else {
1465      const prevProps =
1466        finishedWork.elementType === finishedWork.type
1467          ? current.memoizedProps
1468          : resolveDefaultProps(finishedWork.type, current.memoizedProps);
1469      const prevState = current.memoizedState;
1470      // We could update instance props and state here,
1471      // but instead we rely on them being set during last render.
1472      // TODO: revisit this when we implement resuming.
1473      if (__DEV__) {
1474        if (
1475          finishedWork.type === finishedWork.elementType &&
1476          !didWarnAboutReassigningProps
1477        ) {
1478          if (instance.props !== finishedWork.memoizedProps) {
1479            console.error(
1480              'Expected %s props to match memoized props before ' +
1481                'componentDidUpdate. ' +
1482                'This might either be because of a bug in React, or because ' +
1483                'a component reassigns its own `this.props`. ' +
1484                'Please file an issue.',
1485              getComponentName(finishedWork.type) || 'instance',
1486            );
1487          }
1488          if (instance.state !== finishedWork.memoizedState) {
1489            console.error(
1490              'Expected %s state to match memoized state before ' +
1491                'componentDidUpdate. ' +
1492                'This might either be because of a bug in React, or because ' +
1493                'a component reassigns its own `this.state`. ' +
1494                'Please file an issue.',
1495              getComponentName(finishedWork.type) || 'instance',
1496            );
1497          }
1498        }
1499      }
1500      if (
1501        enableProfilerTimer &&
1502        enableProfilerCommitHooks &&
1503        finishedWork.mode & ProfileMode
1504      ) {
1505        try {
1506          startLayoutEffectTimer();
1507          instance.componentDidUpdate(
1508            prevProps,
1509            prevState,
1510            instance.__reactInternalSnapshotBeforeUpdate,
1511          );
1512        } finally {
1513          recordLayoutEffectDuration(finishedWork);
1514        }
1515      } else {
1516        instance.componentDidUpdate(
1517          prevProps,
1518          prevState,
1519          instance.__reactInternalSnapshotBeforeUpdate,
1520        );
1521      }
1522    }
1523  }
1524
1525  // TODO: I think this is now always non-null by the time it reaches the
1526  // commit phase. Consider removing the type check.
1527  const updateQueue: UpdateQueue<*> | null = (finishedWork.updateQueue: any);
1528  if (updateQueue !== null) {
1529    if (__DEV__) {
1530      if (
1531        finishedWork.type === finishedWork.elementType &&
1532        !didWarnAboutReassigningProps
1533      ) {
1534        if (instance.props !== finishedWork.memoizedProps) {
1535          console.error(
1536            'Expected %s props to match memoized props before ' +
1537              'processing the update queue. ' +
1538              'This might either be because of a bug in React, or because ' +
1539              'a component reassigns its own `this.props`. ' +
1540              'Please file an issue.',
1541            getComponentName(finishedWork.type) || 'instance',
1542          );
1543        }
1544        if (instance.state !== finishedWork.memoizedState) {
1545          console.error(
1546            'Expected %s state to match memoized state before ' +
1547              'processing the update queue. ' +
1548              'This might either be because of a bug in React, or because ' +
1549              'a component reassigns its own `this.state`. ' +
1550              'Please file an issue.',
1551            getComponentName(finishedWork.type) || 'instance',
1552          );
1553        }
1554      }
1555    }
1556    // We could update instance props and state here,
1557    // but instead we rely on them being set during last render.
1558    // TODO: revisit this when we implement resuming.
1559    commitUpdateQueue(finishedWork, updateQueue, instance);
1560  }
1561}
1562
1563/** @noinline */
1564function commitLayoutEffectsForHostRoot(finishedWork: Fiber) {
1565  // TODO: I think this is now always non-null by the time it reaches the
1566  // commit phase. Consider removing the type check.
1567  const updateQueue: UpdateQueue<*> | null = (finishedWork.updateQueue: any);
1568  if (updateQueue !== null) {
1569    let instance = null;
1570    if (finishedWork.child !== null) {
1571      switch (finishedWork.child.tag) {
1572        case HostComponent:
1573          instance = getPublicInstance(finishedWork.child.stateNode);
1574          break;
1575        case ClassComponent:
1576          instance = finishedWork.child.stateNode;
1577          break;
1578      }
1579    }
1580    commitUpdateQueue(finishedWork, updateQueue, instance);
1581  }
1582}
1583
1584/** @noinline */
1585function commitLayoutEffectsForHostComponent(finishedWork: Fiber) {
1586  const instance: Instance = finishedWork.stateNode;
1587  const current = finishedWork.alternate;
1588
1589  // Renderers may schedule work to be done after host components are mounted
1590  // (eg DOM renderer may schedule auto-focus for inputs and form controls).
1591  // These effects should only be committed when components are first mounted,
1592  // aka when there is no current/alternate.
1593  if (current === null && finishedWork.flags & Update) {
1594    const type = finishedWork.type;
1595    const props = finishedWork.memoizedProps;
1596    commitMount(instance, type, props, finishedWork);
1597  }
1598}
1599
1600/** @noinline */
1601function hideOrUnhideAllChildren(finishedWork, isHidden) {
1602  if (supportsMutation) {
1603    // We only have the top Fiber that was inserted but we need to recurse down its
1604    // children to find all the terminal nodes.
1605    let node: Fiber = finishedWork;
1606    while (true) {
1607      if (node.tag === HostComponent) {
1608        const instance = node.stateNode;
1609        if (isHidden) {
1610          hideInstance(instance);
1611        } else {
1612          unhideInstance(node.stateNode, node.memoizedProps);
1613        }
1614      } else if (node.tag === HostText) {
1615        const instance = node.stateNode;
1616        if (isHidden) {
1617          hideTextInstance(instance);
1618        } else {
1619          unhideTextInstance(instance, node.memoizedProps);
1620        }
1621      } else if (
1622        (node.tag === OffscreenComponent ||
1623          node.tag === LegacyHiddenComponent) &&
1624        (node.memoizedState: OffscreenState) !== null &&
1625        node !== finishedWork
1626      ) {
1627        // Found a nested Offscreen component that is hidden. Don't search
1628        // any deeper. This tree should remain hidden.
1629      } else if (node.child !== null) {
1630        node.child.return = node;
1631        node = node.child;
1632        continue;
1633      }
1634      if (node === finishedWork) {
1635        return;
1636      }
1637      while (node.sibling === null) {
1638        if (node.return === null || node.return === finishedWork) {
1639          return;
1640        }
1641        node = node.return;
1642      }
1643      node.sibling.return = node.return;
1644      node = node.sibling;
1645    }
1646  }
1647}
1648
1649export function commitPassiveMountEffects(
1650  root: FiberRoot,
1651  firstChild: Fiber,
1652): void {
1653  if (enableRecursiveCommitTraversal) {
1654    recursivelyCommitPassiveMountEffects(root, firstChild);
1655  } else {
1656    nextEffect = firstChild;
1657    iterativelyCommitPassiveMountEffects_begin(firstChild, root);
1658  }
1659}
1660
1661function recursivelyCommitPassiveMountEffects(
1662  root: FiberRoot,
1663  firstChild: Fiber,
1664): void {
1665  let fiber = firstChild;
1666  while (fiber !== null) {
1667    let prevProfilerOnStack = null;
1668    if (enableProfilerTimer && enableProfilerCommitHooks) {
1669      if (fiber.tag === Profiler) {
1670        prevProfilerOnStack = nearestProfilerOnStack;
1671        nearestProfilerOnStack = fiber;
1672      }
1673    }
1674
1675    const primarySubtreeFlags = fiber.subtreeFlags & PassiveMask;
1676
1677    if (fiber.child !== null && primarySubtreeFlags !== NoFlags) {
1678      recursivelyCommitPassiveMountEffects(root, fiber.child);
1679    }
1680
1681    if ((fiber.flags & Passive) !== NoFlags) {
1682      if (__DEV__) {
1683        setCurrentDebugFiberInDEV(fiber);
1684        invokeGuardedCallback(
1685          null,
1686          commitPassiveMountOnFiber,
1687          null,
1688          root,
1689          fiber,
1690        );
1691        if (hasCaughtError()) {
1692          const error = clearCaughtError();
1693          captureCommitPhaseError(fiber, fiber.return, error);
1694        }
1695        resetCurrentDebugFiberInDEV();
1696      } else {
1697        try {
1698          commitPassiveMountOnFiber(root, fiber);
1699        } catch (error) {
1700          captureCommitPhaseError(fiber, fiber.return, error);
1701        }
1702      }
1703    }
1704
1705    if (enableProfilerTimer && enableProfilerCommitHooks) {
1706      if (fiber.tag === Profiler) {
1707        // Bubble times to the next nearest ancestor Profiler.
1708        // After we process that Profiler, we'll bubble further up.
1709        if (prevProfilerOnStack !== null) {
1710          prevProfilerOnStack.stateNode.passiveEffectDuration +=
1711            fiber.stateNode.passiveEffectDuration;
1712        }
1713
1714        nearestProfilerOnStack = prevProfilerOnStack;
1715      }
1716    }
1717
1718    fiber = fiber.sibling;
1719  }
1720}
1721
1722function iterativelyCommitPassiveMountEffects_begin(
1723  subtreeRoot: Fiber,
1724  root: FiberRoot,
1725) {
1726  while (nextEffect !== null) {
1727    const fiber = nextEffect;
1728    const firstChild = fiber.child;
1729    if ((fiber.subtreeFlags & PassiveMask) !== NoFlags && firstChild !== null) {
1730      if (
1731        enableProfilerTimer &&
1732        enableProfilerCommitHooks &&
1733        fiber.tag === Profiler
1734      ) {
1735        const prevProfilerOnStack = nearestProfilerOnStack;
1736        nearestProfilerOnStack = fiber;
1737
1738        let child = firstChild;
1739        while (child !== null) {
1740          nextEffect = child;
1741          iterativelyCommitPassiveMountEffects_begin(child, root);
1742          child = child.sibling;
1743        }
1744        nextEffect = fiber;
1745
1746        if ((fiber.flags & PassiveMask) !== NoFlags) {
1747          if (__DEV__) {
1748            setCurrentDebugFiberInDEV(fiber);
1749            invokeGuardedCallback(
1750              null,
1751              commitProfilerPassiveEffect,
1752              null,
1753              root,
1754              fiber,
1755            );
1756            if (hasCaughtError()) {
1757              const error = clearCaughtError();
1758              captureCommitPhaseError(fiber, fiber.return, error);
1759            }
1760            resetCurrentDebugFiberInDEV();
1761          } else {
1762            try {
1763              commitProfilerPassiveEffect(root, fiber);
1764            } catch (error) {
1765              captureCommitPhaseError(fiber, fiber.return, error);
1766            }
1767          }
1768        }
1769
1770        // Bubble times to the next nearest ancestor Profiler.
1771        // After we process that Profiler, we'll bubble further up.
1772        if (prevProfilerOnStack !== null) {
1773          prevProfilerOnStack.stateNode.passiveEffectDuration +=
1774            fiber.stateNode.passiveEffectDuration;
1775        }
1776
1777        nearestProfilerOnStack = prevProfilerOnStack;
1778
1779        if (fiber === subtreeRoot) {
1780          nextEffect = null;
1781          return;
1782        }
1783        const sibling = fiber.sibling;
1784        if (sibling !== null) {
1785          sibling.return = fiber.return;
1786          nextEffect = sibling;
1787        } else {
1788          nextEffect = fiber.return;
1789          iterativelyCommitPassiveMountEffects_complete(subtreeRoot, root);
1790        }
1791      } else {
1792        firstChild.return = fiber;
1793        nextEffect = firstChild;
1794      }
1795    } else {
1796      iterativelyCommitPassiveMountEffects_complete(subtreeRoot, root);
1797    }
1798  }
1799}
1800
1801function iterativelyCommitPassiveMountEffects_complete(
1802  subtreeRoot: Fiber,
1803  root: FiberRoot,
1804) {
1805  while (nextEffect !== null) {
1806    const fiber = nextEffect;
1807    if ((fiber.flags & Passive) !== NoFlags) {
1808      if (__DEV__) {
1809        setCurrentDebugFiberInDEV(fiber);
1810        invokeGuardedCallback(
1811          null,
1812          commitPassiveMountOnFiber,
1813          null,
1814          root,
1815          fiber,
1816        );
1817        if (hasCaughtError()) {
1818          const error = clearCaughtError();
1819          captureCommitPhaseError(fiber, fiber.return, error);
1820        }
1821        resetCurrentDebugFiberInDEV();
1822      } else {
1823        try {
1824          commitPassiveMountOnFiber(root, fiber);
1825        } catch (error) {
1826          captureCommitPhaseError(fiber, fiber.return, error);
1827        }
1828      }
1829    }
1830
1831    if (fiber === subtreeRoot) {
1832      nextEffect = null;
1833      return;
1834    }
1835
1836    const sibling = fiber.sibling;
1837    if (sibling !== null) {
1838      sibling.return = fiber.return;
1839      nextEffect = sibling;
1840      return;
1841    }
1842
1843    nextEffect = fiber.return;
1844  }
1845}
1846
1847export function commitPassiveUnmountEffects(firstChild: Fiber): void {
1848  if (enableRecursiveCommitTraversal) {
1849    recursivelyCommitPassiveUnmountEffects(firstChild);
1850  } else {
1851    nextEffect = firstChild;
1852    iterativelyCommitPassiveUnmountEffects_begin();
1853  }
1854}
1855
1856function recursivelyCommitPassiveUnmountEffects(firstChild: Fiber): void {
1857  let fiber = firstChild;
1858  while (fiber !== null) {
1859    const deletions = fiber.deletions;
1860    if (deletions !== null) {
1861      for (let i = 0; i < deletions.length; i++) {
1862        const fiberToDelete = deletions[i];
1863        recursivelyCommitPassiveUnmountEffectsInsideOfDeletedTree(
1864          fiberToDelete,
1865          fiber,
1866        );
1867
1868        // Now that passive effects have been processed, it's safe to detach lingering pointers.
1869        detachFiberAfterEffects(fiberToDelete);
1870      }
1871    }
1872
1873    const child = fiber.child;
1874    if (child !== null) {
1875      // If any children have passive effects then traverse the subtree.
1876      // Note that this requires checking subtreeFlags of the current Fiber,
1877      // rather than the subtreeFlags/effectsTag of the first child,
1878      // since that would not cover passive effects in siblings.
1879      const passiveFlags = fiber.subtreeFlags & PassiveMask;
1880      if (passiveFlags !== NoFlags) {
1881        recursivelyCommitPassiveUnmountEffects(child);
1882      }
1883    }
1884
1885    const primaryFlags = fiber.flags & Passive;
1886    if (primaryFlags !== NoFlags) {
1887      setCurrentDebugFiberInDEV(fiber);
1888      commitPassiveUnmountOnFiber(fiber);
1889      resetCurrentDebugFiberInDEV();
1890    }
1891
1892    fiber = fiber.sibling;
1893  }
1894}
1895
1896function iterativelyCommitPassiveUnmountEffects_begin() {
1897  while (nextEffect !== null) {
1898    const fiber = nextEffect;
1899    const child = fiber.child;
1900
1901    // TODO: Should wrap this in flags check, too, as optimization
1902    const deletions = fiber.deletions;
1903    if (deletions !== null) {
1904      for (let i = 0; i < deletions.length; i++) {
1905        const fiberToDelete = deletions[i];
1906        nextEffect = fiberToDelete;
1907        iterativelyCommitPassiveUnmountEffectsInsideOfDeletedTree_begin(
1908          fiberToDelete,
1909          fiber,
1910        );
1911
1912        // Now that passive effects have been processed, it's safe to detach lingering pointers.
1913        detachFiberAfterEffects(fiberToDelete);
1914      }
1915      nextEffect = fiber;
1916    }
1917
1918    if ((fiber.subtreeFlags & PassiveMask) !== NoFlags && child !== null) {
1919      child.return = fiber;
1920      nextEffect = child;
1921    } else {
1922      iterativelyCommitPassiveUnmountEffects_complete();
1923    }
1924  }
1925}
1926
1927function iterativelyCommitPassiveUnmountEffects_complete() {
1928  while (nextEffect !== null) {
1929    const fiber = nextEffect;
1930    if ((fiber.flags & Passive) !== NoFlags) {
1931      setCurrentDebugFiberInDEV(fiber);
1932      commitPassiveUnmountOnFiber(fiber);
1933      resetCurrentDebugFiberInDEV();
1934    }
1935
1936    const sibling = fiber.sibling;
1937    if (sibling !== null) {
1938      sibling.return = fiber.return;
1939      nextEffect = sibling;
1940      return;
1941    }
1942
1943    nextEffect = fiber.return;
1944  }
1945}
1946
1947function recursivelyCommitPassiveUnmountEffectsInsideOfDeletedTree(
1948  fiberToDelete: Fiber,
1949  nearestMountedAncestor: Fiber,
1950): void {
1951  if ((fiberToDelete.subtreeFlags & PassiveStatic) !== NoFlags) {
1952    // If any children have passive effects then traverse the subtree.
1953    // Note that this requires checking subtreeFlags of the current Fiber,
1954    // rather than the subtreeFlags/effectsTag of the first child,
1955    // since that would not cover passive effects in siblings.
1956    let child = fiberToDelete.child;
1957    while (child !== null) {
1958      recursivelyCommitPassiveUnmountEffectsInsideOfDeletedTree(
1959        child,
1960        nearestMountedAncestor,
1961      );
1962      child = child.sibling;
1963    }
1964  }
1965
1966  if ((fiberToDelete.flags & PassiveStatic) !== NoFlags) {
1967    setCurrentDebugFiberInDEV(fiberToDelete);
1968    commitPassiveUnmountInsideDeletedTreeOnFiber(
1969      fiberToDelete,
1970      nearestMountedAncestor,
1971    );
1972    resetCurrentDebugFiberInDEV();
1973  }
1974}
1975
1976function iterativelyCommitPassiveUnmountEffectsInsideOfDeletedTree_begin(
1977  deletedSubtreeRoot: Fiber,
1978  nearestMountedAncestor: Fiber,
1979) {
1980  while (nextEffect !== null) {
1981    const fiber = nextEffect;
1982    const child = fiber.child;
1983    if ((fiber.subtreeFlags & PassiveStatic) !== NoFlags && child !== null) {
1984      child.return = fiber;
1985      nextEffect = child;
1986    } else {
1987      iterativelyCommitPassiveUnmountEffectsInsideOfDeletedTree_complete(
1988        deletedSubtreeRoot,
1989        nearestMountedAncestor,
1990      );
1991    }
1992  }
1993}
1994
1995function iterativelyCommitPassiveUnmountEffectsInsideOfDeletedTree_complete(
1996  deletedSubtreeRoot: Fiber,
1997  nearestMountedAncestor: Fiber,
1998) {
1999  while (nextEffect !== null) {
2000    const fiber = nextEffect;
2001    if ((fiber.flags & PassiveStatic) !== NoFlags) {
2002      setCurrentDebugFiberInDEV(fiber);
2003      commitPassiveUnmountInsideDeletedTreeOnFiber(
2004        fiber,
2005        nearestMountedAncestor,
2006      );
2007      resetCurrentDebugFiberInDEV();
2008    }
2009
2010    if (fiber === deletedSubtreeRoot) {
2011      nextEffect = null;
2012      return;
2013    }
2014
2015    const sibling = fiber.sibling;
2016    if (sibling !== null) {
2017      sibling.return = fiber.return;
2018      nextEffect = sibling;
2019      return;
2020    }
2021
2022    nextEffect = fiber.return;
2023  }
2024}
2025
2026function detachFiberAfterEffects(fiber: Fiber): void {
2027  // Null out fields to improve GC for references that may be lingering (e.g. DevTools).
2028  // Note that we already cleared the return pointer in detachFiberMutation().
2029  fiber.child = null;
2030  fiber.deletions = null;
2031  fiber.dependencies = null;
2032  fiber.memoizedProps = null;
2033  fiber.memoizedState = null;
2034  fiber.pendingProps = null;
2035  fiber.sibling = null;
2036  fiber.stateNode = null;
2037  fiber.updateQueue = null;
2038
2039  if (__DEV__) {
2040    fiber._debugOwner = null;
2041  }
2042}
2043
2044function commitAttachRef(finishedWork: Fiber) {
2045  const ref = finishedWork.ref;
2046  if (ref !== null) {
2047    const instance = finishedWork.stateNode;
2048    let instanceToUse;
2049    switch (finishedWork.tag) {
2050      case HostComponent:
2051        instanceToUse = getPublicInstance(instance);
2052        break;
2053      default:
2054        instanceToUse = instance;
2055    }
2056    // Moved outside to ensure DCE works with this flag
2057    if (enableScopeAPI && finishedWork.tag === ScopeComponent) {
2058      instanceToUse = instance;
2059    }
2060    if (typeof ref === 'function') {
2061      if (
2062        enableProfilerTimer &&
2063        enableProfilerCommitHooks &&
2064        finishedWork.mode & ProfileMode
2065      ) {
2066        try {
2067          startLayoutEffectTimer();
2068          ref(instanceToUse);
2069        } finally {
2070          recordLayoutEffectDuration(finishedWork);
2071        }
2072      } else {
2073        ref(instanceToUse);
2074      }
2075    } else {
2076      if (__DEV__) {
2077        if (!ref.hasOwnProperty('current')) {
2078          console.error(
2079            'Unexpected ref object provided for %s. ' +
2080              'Use either a ref-setter function or React.createRef().',
2081            getComponentName(finishedWork.type),
2082          );
2083        }
2084      }
2085
2086      ref.current = instanceToUse;
2087    }
2088  }
2089}
2090
2091function commitDetachRef(current: Fiber) {
2092  const currentRef = current.ref;
2093  if (currentRef !== null) {
2094    if (typeof currentRef === 'function') {
2095      if (
2096        enableProfilerTimer &&
2097        enableProfilerCommitHooks &&
2098        current.mode & ProfileMode
2099      ) {
2100        try {
2101          startLayoutEffectTimer();
2102          currentRef(null);
2103        } finally {
2104          recordLayoutEffectDuration(current);
2105        }
2106      } else {
2107        currentRef(null);
2108      }
2109    } else {
2110      currentRef.current = null;
2111    }
2112  }
2113}
2114
2115// User-originating errors (lifecycles and refs) should not interrupt
2116// deletion, so don't let them throw. Host-originating errors should
2117// interrupt deletion, so it's okay
2118function commitUnmount(
2119  finishedRoot: FiberRoot,
2120  current: Fiber,
2121  nearestMountedAncestor: Fiber,
2122  renderPriorityLevel: ReactPriorityLevel,
2123): void {
2124  onCommitUnmount(current);
2125
2126  switch (current.tag) {
2127    case FunctionComponent:
2128    case ForwardRef:
2129    case MemoComponent:
2130    case SimpleMemoComponent: {
2131      const updateQueue: FunctionComponentUpdateQueue | null = (current.updateQueue: any);
2132      if (updateQueue !== null) {
2133        const lastEffect = updateQueue.lastEffect;
2134        if (lastEffect !== null) {
2135          const firstEffect = lastEffect.next;
2136
2137          let effect = firstEffect;
2138          do {
2139            const {destroy, tag} = effect;
2140            if (destroy !== undefined) {
2141              if ((tag & HookLayout) !== NoHookEffect) {
2142                if (
2143                  enableProfilerTimer &&
2144                  enableProfilerCommitHooks &&
2145                  current.mode & ProfileMode
2146                ) {
2147                  startLayoutEffectTimer();
2148                  safelyCallDestroy(current, nearestMountedAncestor, destroy);
2149                  recordLayoutEffectDuration(current);
2150                } else {
2151                  safelyCallDestroy(current, nearestMountedAncestor, destroy);
2152                }
2153              }
2154            }
2155            effect = effect.next;
2156          } while (effect !== firstEffect);
2157        }
2158      }
2159      return;
2160    }
2161    case ClassComponent: {
2162      safelyDetachRef(current, nearestMountedAncestor);
2163      const instance = current.stateNode;
2164      if (typeof instance.componentWillUnmount === 'function') {
2165        safelyCallComponentWillUnmount(
2166          current,
2167          instance,
2168          nearestMountedAncestor,
2169        );
2170      }
2171      return;
2172    }
2173    case HostComponent: {
2174      safelyDetachRef(current, nearestMountedAncestor);
2175      return;
2176    }
2177    case HostPortal: {
2178      // TODO: this is recursive.
2179      // We are also not using this parent because
2180      // the portal will get pushed immediately.
2181      if (supportsMutation) {
2182        unmountHostComponents(
2183          finishedRoot,
2184          current,
2185          nearestMountedAncestor,
2186          renderPriorityLevel,
2187        );
2188      } else if (supportsPersistence) {
2189        emptyPortalContainer(current);
2190      }
2191      return;
2192    }
2193    case FundamentalComponent: {
2194      if (enableFundamentalAPI) {
2195        const fundamentalInstance = current.stateNode;
2196        if (fundamentalInstance !== null) {
2197          unmountFundamentalComponent(fundamentalInstance);
2198          current.stateNode = null;
2199        }
2200      }
2201      return;
2202    }
2203    case DehydratedFragment: {
2204      if (enableSuspenseCallback) {
2205        const hydrationCallbacks = finishedRoot.hydrationCallbacks;
2206        if (hydrationCallbacks !== null) {
2207          const onDeleted = hydrationCallbacks.onDeleted;
2208          if (onDeleted) {
2209            onDeleted((current.stateNode: SuspenseInstance));
2210          }
2211        }
2212      }
2213      return;
2214    }
2215    case ScopeComponent: {
2216      if (enableScopeAPI) {
2217        safelyDetachRef(current, nearestMountedAncestor);
2218      }
2219      return;
2220    }
2221  }
2222}
2223
2224function commitNestedUnmounts(
2225  finishedRoot: FiberRoot,
2226  root: Fiber,
2227  nearestMountedAncestor: Fiber,
2228  renderPriorityLevel: ReactPriorityLevel,
2229): void {
2230  // While we're inside a removed host node we don't want to call
2231  // removeChild on the inner nodes because they're removed by the top
2232  // call anyway. We also want to call componentWillUnmount on all
2233  // composites before this host node is removed from the tree. Therefore
2234  // we do an inner loop while we're still inside the host node.
2235  let node: Fiber = root;
2236  while (true) {
2237    commitUnmount(
2238      finishedRoot,
2239      node,
2240      nearestMountedAncestor,
2241      renderPriorityLevel,
2242    );
2243    // Visit children because they may contain more composite or host nodes.
2244    // Skip portals because commitUnmount() currently visits them recursively.
2245    if (
2246      node.child !== null &&
2247      // If we use mutation we drill down into portals using commitUnmount above.
2248      // If we don't use mutation we drill down into portals here instead.
2249      (!supportsMutation || node.tag !== HostPortal)
2250    ) {
2251      node.child.return = node;
2252      node = node.child;
2253      continue;
2254    }
2255    if (node === root) {
2256      return;
2257    }
2258    while (node.sibling === null) {
2259      if (node.return === null || node.return === root) {
2260        return;
2261      }
2262      node = node.return;
2263    }
2264    node.sibling.return = node.return;
2265    node = node.sibling;
2266  }
2267}
2268
2269function detachFiberMutation(fiber: Fiber) {
2270  // Cut off the return pointer to disconnect it from the tree.
2271  // This enables us to detect and warn against state updates on an unmounted component.
2272  // It also prevents events from bubbling from within disconnected components.
2273  //
2274  // Ideally, we should also clear the child pointer of the parent alternate to let this
2275  // get GC:ed but we don't know which for sure which parent is the current
2276  // one so we'll settle for GC:ing the subtree of this child.
2277  // This child itself will be GC:ed when the parent updates the next time.
2278  //
2279  // Note that we can't clear child or sibling pointers yet.
2280  // They're needed for passive effects and for findDOMNode.
2281  // We defer those fields, and all other cleanup, to the passive phase (see detachFiberAfterEffects).
2282  const alternate = fiber.alternate;
2283  if (alternate !== null) {
2284    alternate.return = null;
2285    fiber.alternate = null;
2286  }
2287  fiber.return = null;
2288}
2289
2290function emptyPortalContainer(current: Fiber) {
2291  if (!supportsPersistence) {
2292    return;
2293  }
2294
2295  const portal: {
2296    containerInfo: Container,
2297    pendingChildren: ChildSet,
2298    ...
2299  } = current.stateNode;
2300  const {containerInfo} = portal;
2301  const emptyChildSet = createContainerChildSet(containerInfo);
2302  replaceContainerChildren(containerInfo, emptyChildSet);
2303}
2304
2305function commitContainer(finishedWork: Fiber) {
2306  if (!supportsPersistence) {
2307    return;
2308  }
2309
2310  switch (finishedWork.tag) {
2311    case ClassComponent:
2312    case HostComponent:
2313    case HostText:
2314    case FundamentalComponent: {
2315      return;
2316    }
2317    case HostRoot:
2318    case HostPortal: {
2319      const portalOrRoot: {
2320        containerInfo: Container,
2321        pendingChildren: ChildSet,
2322        ...
2323      } = finishedWork.stateNode;
2324      const {containerInfo, pendingChildren} = portalOrRoot;
2325      replaceContainerChildren(containerInfo, pendingChildren);
2326      return;
2327    }
2328  }
2329  invariant(
2330    false,
2331    'This unit of work tag should not have side-effects. This error is ' +
2332      'likely caused by a bug in React. Please file an issue.',
2333  );
2334}
2335
2336function getHostParentFiber(fiber: Fiber): Fiber {
2337  let parent = fiber.return;
2338  while (parent !== null) {
2339    if (isHostParent(parent)) {
2340      return parent;
2341    }
2342    parent = parent.return;
2343  }
2344  invariant(
2345    false,
2346    'Expected to find a host parent. This error is likely caused by a bug ' +
2347      'in React. Please file an issue.',
2348  );
2349}
2350
2351function isHostParent(fiber: Fiber): boolean {
2352  return (
2353    fiber.tag === HostComponent ||
2354    fiber.tag === HostRoot ||
2355    fiber.tag === HostPortal
2356  );
2357}
2358
2359function getHostSibling(fiber: Fiber): ?Instance {
2360  // We're going to search forward into the tree until we find a sibling host
2361  // node. Unfortunately, if multiple insertions are done in a row we have to
2362  // search past them. This leads to exponential search for the next sibling.
2363  // TODO: Find a more efficient way to do this.
2364  let node: Fiber = fiber;
2365  siblings: while (true) {
2366    // If we didn't find anything, let's try the next sibling.
2367    while (node.sibling === null) {
2368      if (node.return === null || isHostParent(node.return)) {
2369        // If we pop out of the root or hit the parent the fiber we are the
2370        // last sibling.
2371        return null;
2372      }
2373      node = node.return;
2374    }
2375    node.sibling.return = node.return;
2376    node = node.sibling;
2377    while (
2378      node.tag !== HostComponent &&
2379      node.tag !== HostText &&
2380      node.tag !== DehydratedFragment
2381    ) {
2382      // If it is not host node and, we might have a host node inside it.
2383      // Try to search down until we find one.
2384      if (node.flags & Placement) {
2385        // If we don't have a child, try the siblings instead.
2386        continue siblings;
2387      }
2388      // If we don't have a child, try the siblings instead.
2389      // We also skip portals because they are not part of this host tree.
2390      if (node.child === null || node.tag === HostPortal) {
2391        continue siblings;
2392      } else {
2393        node.child.return = node;
2394        node = node.child;
2395      }
2396    }
2397    // Check if this host node is stable or about to be placed.
2398    if (!(node.flags & Placement)) {
2399      // Found it!
2400      return node.stateNode;
2401    }
2402  }
2403}
2404
2405function commitPlacement(finishedWork: Fiber): void {
2406  if (!supportsMutation) {
2407    return;
2408  }
2409
2410  // Recursively insert all host nodes into the parent.
2411  const parentFiber = getHostParentFiber(finishedWork);
2412
2413  // Note: these two variables *must* always be updated together.
2414  let parent;
2415  let isContainer;
2416  const parentStateNode = parentFiber.stateNode;
2417  switch (parentFiber.tag) {
2418    case HostComponent:
2419      parent = parentStateNode;
2420      isContainer = false;
2421      break;
2422    case HostRoot:
2423      parent = parentStateNode.containerInfo;
2424      isContainer = true;
2425      break;
2426    case HostPortal:
2427      parent = parentStateNode.containerInfo;
2428      isContainer = true;
2429      break;
2430    case FundamentalComponent:
2431      if (enableFundamentalAPI) {
2432        parent = parentStateNode.instance;
2433        isContainer = false;
2434      }
2435    // eslint-disable-next-line-no-fallthrough
2436    default:
2437      invariant(
2438        false,
2439        'Invalid host parent fiber. This error is likely caused by a bug ' +
2440          'in React. Please file an issue.',
2441      );
2442  }
2443  if (parentFiber.flags & ContentReset) {
2444    // Reset the text content of the parent before doing any insertions
2445    resetTextContent(parent);
2446    // Clear ContentReset from the effect tag
2447    parentFiber.flags &= ~ContentReset;
2448  }
2449
2450  const before = getHostSibling(finishedWork);
2451  // We only have the top Fiber that was inserted but we need to recurse down its
2452  // children to find all the terminal nodes.
2453  if (isContainer) {
2454    insertOrAppendPlacementNodeIntoContainer(finishedWork, before, parent);
2455  } else {
2456    insertOrAppendPlacementNode(finishedWork, before, parent);
2457  }
2458}
2459
2460function insertOrAppendPlacementNodeIntoContainer(
2461  node: Fiber,
2462  before: ?Instance,
2463  parent: Container,
2464): void {
2465  const {tag} = node;
2466  const isHost = tag === HostComponent || tag === HostText;
2467  if (isHost || (enableFundamentalAPI && tag === FundamentalComponent)) {
2468    const stateNode = isHost ? node.stateNode : node.stateNode.instance;
2469    if (before) {
2470      insertInContainerBefore(parent, stateNode, before);
2471    } else {
2472      appendChildToContainer(parent, stateNode);
2473    }
2474  } else if (tag === HostPortal) {
2475    // If the insertion itself is a portal, then we don't want to traverse
2476    // down its children. Instead, we'll get insertions from each child in
2477    // the portal directly.
2478  } else {
2479    const child = node.child;
2480    if (child !== null) {
2481      insertOrAppendPlacementNodeIntoContainer(child, before, parent);
2482      let sibling = child.sibling;
2483      while (sibling !== null) {
2484        insertOrAppendPlacementNodeIntoContainer(sibling, before, parent);
2485        sibling = sibling.sibling;
2486      }
2487    }
2488  }
2489}
2490
2491function insertOrAppendPlacementNode(
2492  node: Fiber,
2493  before: ?Instance,
2494  parent: Instance,
2495): void {
2496  const {tag} = node;
2497  const isHost = tag === HostComponent || tag === HostText;
2498  if (isHost || (enableFundamentalAPI && tag === FundamentalComponent)) {
2499    const stateNode = isHost ? node.stateNode : node.stateNode.instance;
2500    if (before) {
2501      insertBefore(parent, stateNode, before);
2502    } else {
2503      appendChild(parent, stateNode);
2504    }
2505  } else if (tag === HostPortal) {
2506    // If the insertion itself is a portal, then we don't want to traverse
2507    // down its children. Instead, we'll get insertions from each child in
2508    // the portal directly.
2509  } else {
2510    const child = node.child;
2511    if (child !== null) {
2512      insertOrAppendPlacementNode(child, before, parent);
2513      let sibling = child.sibling;
2514      while (sibling !== null) {
2515        insertOrAppendPlacementNode(sibling, before, parent);
2516        sibling = sibling.sibling;
2517      }
2518    }
2519  }
2520}
2521
2522function unmountHostComponents(
2523  finishedRoot: FiberRoot,
2524  current: Fiber,
2525  nearestMountedAncestor: Fiber,
2526  renderPriorityLevel: ReactPriorityLevel,
2527): void {
2528  // We only have the top Fiber that was deleted but we need to recurse down its
2529  // children to find all the terminal nodes.
2530  let node: Fiber = current;
2531
2532  // Each iteration, currentParent is populated with node's host parent if not
2533  // currentParentIsValid.
2534  let currentParentIsValid = false;
2535
2536  // Note: these two variables *must* always be updated together.
2537  let currentParent;
2538  let currentParentIsContainer;
2539
2540  while (true) {
2541    if (!currentParentIsValid) {
2542      let parent = node.return;
2543      findParent: while (true) {
2544        invariant(
2545          parent !== null,
2546          'Expected to find a host parent. This error is likely caused by ' +
2547            'a bug in React. Please file an issue.',
2548        );
2549        const parentStateNode = parent.stateNode;
2550        switch (parent.tag) {
2551          case HostComponent:
2552            currentParent = parentStateNode;
2553            currentParentIsContainer = false;
2554            break findParent;
2555          case HostRoot:
2556            currentParent = parentStateNode.containerInfo;
2557            currentParentIsContainer = true;
2558            break findParent;
2559          case HostPortal:
2560            currentParent = parentStateNode.containerInfo;
2561            currentParentIsContainer = true;
2562            break findParent;
2563          case FundamentalComponent:
2564            if (enableFundamentalAPI) {
2565              currentParent = parentStateNode.instance;
2566              currentParentIsContainer = false;
2567            }
2568        }
2569        parent = parent.return;
2570      }
2571      currentParentIsValid = true;
2572    }
2573
2574    if (node.tag === HostComponent || node.tag === HostText) {
2575      commitNestedUnmounts(
2576        finishedRoot,
2577        node,
2578        nearestMountedAncestor,
2579        renderPriorityLevel,
2580      );
2581      // After all the children have unmounted, it is now safe to remove the
2582      // node from the tree.
2583      if (currentParentIsContainer) {
2584        removeChildFromContainer(
2585          ((currentParent: any): Container),
2586          (node.stateNode: Instance | TextInstance),
2587        );
2588      } else {
2589        removeChild(
2590          ((currentParent: any): Instance),
2591          (node.stateNode: Instance | TextInstance),
2592        );
2593      }
2594      // Don't visit children because we already visited them.
2595    } else if (enableFundamentalAPI && node.tag === FundamentalComponent) {
2596      const fundamentalNode = node.stateNode.instance;
2597      commitNestedUnmounts(
2598        finishedRoot,
2599        node,
2600        nearestMountedAncestor,
2601        renderPriorityLevel,
2602      );
2603      // After all the children have unmounted, it is now safe to remove the
2604      // node from the tree.
2605      if (currentParentIsContainer) {
2606        removeChildFromContainer(
2607          ((currentParent: any): Container),
2608          (fundamentalNode: Instance),
2609        );
2610      } else {
2611        removeChild(
2612          ((currentParent: any): Instance),
2613          (fundamentalNode: Instance),
2614        );
2615      }
2616    } else if (
2617      enableSuspenseServerRenderer &&
2618      node.tag === DehydratedFragment
2619    ) {
2620      if (enableSuspenseCallback) {
2621        const hydrationCallbacks = finishedRoot.hydrationCallbacks;
2622        if (hydrationCallbacks !== null) {
2623          const onDeleted = hydrationCallbacks.onDeleted;
2624          if (onDeleted) {
2625            onDeleted((node.stateNode: SuspenseInstance));
2626          }
2627        }
2628      }
2629
2630      // Delete the dehydrated suspense boundary and all of its content.
2631      if (currentParentIsContainer) {
2632        clearSuspenseBoundaryFromContainer(
2633          ((currentParent: any): Container),
2634          (node.stateNode: SuspenseInstance),
2635        );
2636      } else {
2637        clearSuspenseBoundary(
2638          ((currentParent: any): Instance),
2639          (node.stateNode: SuspenseInstance),
2640        );
2641      }
2642    } else if (node.tag === HostPortal) {
2643      if (node.child !== null) {
2644        // When we go into a portal, it becomes the parent to remove from.
2645        // We will reassign it back when we pop the portal on the way up.
2646        currentParent = node.stateNode.containerInfo;
2647        currentParentIsContainer = true;
2648        // Visit children because portals might contain host components.
2649        node.child.return = node;
2650        node = node.child;
2651        continue;
2652      }
2653    } else {
2654      commitUnmount(
2655        finishedRoot,
2656        node,
2657        nearestMountedAncestor,
2658        renderPriorityLevel,
2659      );
2660      // Visit children because we may find more host components below.
2661      if (node.child !== null) {
2662        node.child.return = node;
2663        node = node.child;
2664        continue;
2665      }
2666    }
2667    if (node === current) {
2668      return;
2669    }
2670    while (node.sibling === null) {
2671      if (node.return === null || node.return === current) {
2672        return;
2673      }
2674      node = node.return;
2675      if (node.tag === HostPortal) {
2676        // When we go out of the portal, we need to restore the parent.
2677        // Since we don't keep a stack of them, we will search for it.
2678        currentParentIsValid = false;
2679      }
2680    }
2681    node.sibling.return = node.return;
2682    node = node.sibling;
2683  }
2684}
2685
2686function commitDeletion(
2687  finishedRoot: FiberRoot,
2688  current: Fiber,
2689  nearestMountedAncestor: Fiber,
2690  renderPriorityLevel: ReactPriorityLevel,
2691): void {
2692  if (supportsMutation) {
2693    // Recursively delete all host nodes from the parent.
2694    // Detach refs and call componentWillUnmount() on the whole subtree.
2695    unmountHostComponents(
2696      finishedRoot,
2697      current,
2698      nearestMountedAncestor,
2699      renderPriorityLevel,
2700    );
2701  } else {
2702    // Detach refs and call componentWillUnmount() on the whole subtree.
2703    commitNestedUnmounts(
2704      finishedRoot,
2705      current,
2706      nearestMountedAncestor,
2707      renderPriorityLevel,
2708    );
2709  }
2710  const alternate = current.alternate;
2711  detachFiberMutation(current);
2712  if (alternate !== null) {
2713    detachFiberMutation(alternate);
2714  }
2715}
2716
2717function commitWork(current: Fiber | null, finishedWork: Fiber): void {
2718  if (!supportsMutation) {
2719    switch (finishedWork.tag) {
2720      case FunctionComponent:
2721      case ForwardRef:
2722      case MemoComponent:
2723      case SimpleMemoComponent: {
2724        // Layout effects are destroyed during the mutation phase so that all
2725        // destroy functions for all fibers are called before any create functions.
2726        // This prevents sibling component effects from interfering with each other,
2727        // e.g. a destroy function in one component should never override a ref set
2728        // by a create function in another component during the same commit.
2729        if (
2730          enableProfilerTimer &&
2731          enableProfilerCommitHooks &&
2732          finishedWork.mode & ProfileMode
2733        ) {
2734          try {
2735            startLayoutEffectTimer();
2736            commitHookEffectListUnmount(
2737              HookLayout | HookHasEffect,
2738              finishedWork,
2739              finishedWork.return,
2740            );
2741          } finally {
2742            recordLayoutEffectDuration(finishedWork);
2743          }
2744        } else {
2745          commitHookEffectListUnmount(
2746            HookLayout | HookHasEffect,
2747            finishedWork,
2748            finishedWork.return,
2749          );
2750        }
2751        return;
2752      }
2753      case Profiler: {
2754        return;
2755      }
2756      case SuspenseComponent: {
2757        commitSuspenseComponent(finishedWork);
2758        attachSuspenseRetryListeners(finishedWork);
2759        return;
2760      }
2761      case SuspenseListComponent: {
2762        attachSuspenseRetryListeners(finishedWork);
2763        return;
2764      }
2765      case HostRoot: {
2766        if (supportsHydration) {
2767          const root: FiberRoot = finishedWork.stateNode;
2768          if (root.hydrate) {
2769            // We've just hydrated. No need to hydrate again.
2770            root.hydrate = false;
2771            commitHydratedContainer(root.containerInfo);
2772          }
2773        }
2774        break;
2775      }
2776      case OffscreenComponent:
2777      case LegacyHiddenComponent: {
2778        return;
2779      }
2780    }
2781
2782    commitContainer(finishedWork);
2783    return;
2784  }
2785
2786  switch (finishedWork.tag) {
2787    case FunctionComponent:
2788    case ForwardRef:
2789    case MemoComponent:
2790    case SimpleMemoComponent: {
2791      // Layout effects are destroyed during the mutation phase so that all
2792      // destroy functions for all fibers are called before any create functions.
2793      // This prevents sibling component effects from interfering with each other,
2794      // e.g. a destroy function in one component should never override a ref set
2795      // by a create function in another component during the same commit.
2796      if (
2797        enableProfilerTimer &&
2798        enableProfilerCommitHooks &&
2799        finishedWork.mode & ProfileMode
2800      ) {
2801        try {
2802          startLayoutEffectTimer();
2803          commitHookEffectListUnmount(
2804            HookLayout | HookHasEffect,
2805            finishedWork,
2806            finishedWork.return,
2807          );
2808        } finally {
2809          recordLayoutEffectDuration(finishedWork);
2810        }
2811      } else {
2812        commitHookEffectListUnmount(
2813          HookLayout | HookHasEffect,
2814          finishedWork,
2815          finishedWork.return,
2816        );
2817      }
2818      return;
2819    }
2820    case ClassComponent: {
2821      return;
2822    }
2823    case HostComponent: {
2824      const instance: Instance = finishedWork.stateNode;
2825      if (instance != null) {
2826        // Commit the work prepared earlier.
2827        const newProps = finishedWork.memoizedProps;
2828        // For hydration we reuse the update path but we treat the oldProps
2829        // as the newProps. The updatePayload will contain the real change in
2830        // this case.
2831        const oldProps = current !== null ? current.memoizedProps : newProps;
2832        const type = finishedWork.type;
2833        // TODO: Type the updateQueue to be specific to host components.
2834        const updatePayload: null | UpdatePayload = (finishedWork.updateQueue: any);
2835        finishedWork.updateQueue = null;
2836        if (updatePayload !== null) {
2837          commitUpdate(
2838            instance,
2839            updatePayload,
2840            type,
2841            oldProps,
2842            newProps,
2843            finishedWork,
2844          );
2845        }
2846      }
2847      return;
2848    }
2849    case HostText: {
2850      invariant(
2851        finishedWork.stateNode !== null,
2852        'This should have a text node initialized. This error is likely ' +
2853          'caused by a bug in React. Please file an issue.',
2854      );
2855      const textInstance: TextInstance = finishedWork.stateNode;
2856      const newText: string = finishedWork.memoizedProps;
2857      // For hydration we reuse the update path but we treat the oldProps
2858      // as the newProps. The updatePayload will contain the real change in
2859      // this case.
2860      const oldText: string =
2861        current !== null ? current.memoizedProps : newText;
2862      commitTextUpdate(textInstance, oldText, newText);
2863      return;
2864    }
2865    case HostRoot: {
2866      if (supportsHydration) {
2867        const root: FiberRoot = finishedWork.stateNode;
2868        if (root.hydrate) {
2869          // We've just hydrated. No need to hydrate again.
2870          root.hydrate = false;
2871          commitHydratedContainer(root.containerInfo);
2872        }
2873      }
2874      return;
2875    }
2876    case Profiler: {
2877      return;
2878    }
2879    case SuspenseComponent: {
2880      commitSuspenseComponent(finishedWork);
2881      attachSuspenseRetryListeners(finishedWork);
2882      return;
2883    }
2884    case SuspenseListComponent: {
2885      attachSuspenseRetryListeners(finishedWork);
2886      return;
2887    }
2888    case IncompleteClassComponent: {
2889      return;
2890    }
2891    case FundamentalComponent: {
2892      if (enableFundamentalAPI) {
2893        const fundamentalInstance = finishedWork.stateNode;
2894        updateFundamentalComponent(fundamentalInstance);
2895        return;
2896      }
2897      break;
2898    }
2899    case ScopeComponent: {
2900      if (enableScopeAPI) {
2901        const scopeInstance = finishedWork.stateNode;
2902        prepareScopeUpdate(scopeInstance, finishedWork);
2903        return;
2904      }
2905      break;
2906    }
2907    case OffscreenComponent:
2908    case LegacyHiddenComponent: {
2909      const newState: OffscreenState | null = finishedWork.memoizedState;
2910      const isHidden = newState !== null;
2911      hideOrUnhideAllChildren(finishedWork, isHidden);
2912      return;
2913    }
2914  }
2915  invariant(
2916    false,
2917    'This unit of work tag should not have side-effects. This error is ' +
2918      'likely caused by a bug in React. Please file an issue.',
2919  );
2920}
2921
2922function commitSuspenseComponent(finishedWork: Fiber) {
2923  const newState: SuspenseState | null = finishedWork.memoizedState;
2924
2925  if (newState !== null) {
2926    markCommitTimeOfFallback();
2927
2928    if (supportsMutation) {
2929      // Hide the Offscreen component that contains the primary children. TODO:
2930      // Ideally, this effect would have been scheduled on the Offscreen fiber
2931      // itself. That's how unhiding works: the Offscreen component schedules an
2932      // effect on itself. However, in this case, the component didn't complete,
2933      // so the fiber was never added to the effect list in the normal path. We
2934      // could have appended it to the effect list in the Suspense component's
2935      // second pass, but doing it this way is less complicated. This would be
2936      // simpler if we got rid of the effect list and traversed the tree, like
2937      // we're planning to do.
2938      const primaryChildParent: Fiber = (finishedWork.child: any);
2939      hideOrUnhideAllChildren(primaryChildParent, true);
2940    }
2941  }
2942
2943  if (enableSuspenseCallback && newState !== null) {
2944    const suspenseCallback = finishedWork.memoizedProps.suspenseCallback;
2945    if (typeof suspenseCallback === 'function') {
2946      const wakeables: Set<Wakeable> | null = (finishedWork.updateQueue: any);
2947      if (wakeables !== null) {
2948        suspenseCallback(new Set(wakeables));
2949      }
2950    } else if (__DEV__) {
2951      if (suspenseCallback !== undefined) {
2952        console.error('Unexpected type for suspenseCallback.');
2953      }
2954    }
2955  }
2956}
2957
2958/** @noinline */
2959function commitSuspenseHydrationCallbacks(
2960  finishedRoot: FiberRoot,
2961  finishedWork: Fiber,
2962) {
2963  if (!supportsHydration) {
2964    return;
2965  }
2966  const newState: SuspenseState | null = finishedWork.memoizedState;
2967  if (newState === null) {
2968    const current = finishedWork.alternate;
2969    if (current !== null) {
2970      const prevState: SuspenseState | null = current.memoizedState;
2971      if (prevState !== null) {
2972        const suspenseInstance = prevState.dehydrated;
2973        if (suspenseInstance !== null) {
2974          commitHydratedSuspenseInstance(suspenseInstance);
2975          if (enableSuspenseCallback) {
2976            const hydrationCallbacks = finishedRoot.hydrationCallbacks;
2977            if (hydrationCallbacks !== null) {
2978              const onHydrated = hydrationCallbacks.onHydrated;
2979              if (onHydrated) {
2980                onHydrated(suspenseInstance);
2981              }
2982            }
2983          }
2984        }
2985      }
2986    }
2987  }
2988}
2989
2990function attachSuspenseRetryListeners(finishedWork: Fiber) {
2991  // If this boundary just timed out, then it will have a set of wakeables.
2992  // For each wakeable, attach a listener so that when it resolves, React
2993  // attempts to re-render the boundary in the primary (pre-timeout) state.
2994  const wakeables: Set<Wakeable> | null = (finishedWork.updateQueue: any);
2995  if (wakeables !== null) {
2996    finishedWork.updateQueue = null;
2997    let retryCache = finishedWork.stateNode;
2998    if (retryCache === null) {
2999      retryCache = finishedWork.stateNode = new PossiblyWeakSet();
3000    }
3001    wakeables.forEach(wakeable => {
3002      // Memoize using the boundary fiber to prevent redundant listeners.
3003      let retry = resolveRetryWakeable.bind(null, finishedWork, wakeable);
3004      if (!retryCache.has(wakeable)) {
3005        if (enableSchedulerTracing) {
3006          if (wakeable.__reactDoNotTraceInteractions !== true) {
3007            retry = Schedule_tracing_wrap(retry);
3008          }
3009        }
3010        retryCache.add(wakeable);
3011        wakeable.then(retry, retry);
3012      }
3013    });
3014  }
3015}
3016
3017// This function detects when a Suspense boundary goes from visible to hidden.
3018// It returns false if the boundary is already hidden.
3019// TODO: Use an effect tag.
3020function isSuspenseBoundaryBeingHidden(
3021  current: Fiber | null,
3022  finishedWork: Fiber,
3023): boolean {
3024  if (current !== null) {
3025    const oldState: SuspenseState | null = current.memoizedState;
3026    if (oldState === null || oldState.dehydrated !== null) {
3027      const newState: SuspenseState | null = finishedWork.memoizedState;
3028      return newState !== null && newState.dehydrated === null;
3029    }
3030  }
3031  return false;
3032}
3033
3034function commitResetTextContent(current: Fiber): void {
3035  if (!supportsMutation) {
3036    return;
3037  }
3038  resetTextContent(current.stateNode);
3039}
3040
3041function commitPassiveUnmountOnFiber(finishedWork: Fiber): void {
3042  switch (finishedWork.tag) {
3043    case FunctionComponent:
3044    case ForwardRef:
3045    case SimpleMemoComponent: {
3046      if (
3047        enableProfilerTimer &&
3048        enableProfilerCommitHooks &&
3049        finishedWork.mode & ProfileMode
3050      ) {
3051        startPassiveEffectTimer();
3052        commitHookEffectListUnmount(
3053          HookPassive | HookHasEffect,
3054          finishedWork,
3055          finishedWork.return,
3056        );
3057        recordPassiveEffectDuration(finishedWork);
3058      } else {
3059        commitHookEffectListUnmount(
3060          HookPassive | HookHasEffect,
3061          finishedWork,
3062          finishedWork.return,
3063        );
3064      }
3065      break;
3066    }
3067  }
3068}
3069
3070function commitPassiveUnmountInsideDeletedTreeOnFiber(
3071  current: Fiber,
3072  nearestMountedAncestor: Fiber | null,
3073): void {
3074  switch (current.tag) {
3075    case FunctionComponent:
3076    case ForwardRef:
3077    case SimpleMemoComponent: {
3078      if (
3079        enableProfilerTimer &&
3080        enableProfilerCommitHooks &&
3081        current.mode & ProfileMode
3082      ) {
3083        startPassiveEffectTimer();
3084        commitHookEffectListUnmount(
3085          HookPassive,
3086          current,
3087          nearestMountedAncestor,
3088        );
3089        recordPassiveEffectDuration(current);
3090      } else {
3091        commitHookEffectListUnmount(
3092          HookPassive,
3093          current,
3094          nearestMountedAncestor,
3095        );
3096      }
3097      break;
3098    }
3099  }
3100}
3101
3102function commitPassiveMountOnFiber(
3103  finishedRoot: FiberRoot,
3104  finishedWork: Fiber,
3105): void {
3106  switch (finishedWork.tag) {
3107    case FunctionComponent:
3108    case ForwardRef:
3109    case SimpleMemoComponent: {
3110      if (
3111        enableProfilerTimer &&
3112        enableProfilerCommitHooks &&
3113        finishedWork.mode & ProfileMode
3114      ) {
3115        startPassiveEffectTimer();
3116        try {
3117          commitHookEffectListMount(HookPassive | HookHasEffect, finishedWork);
3118        } finally {
3119          recordPassiveEffectDuration(finishedWork);
3120        }
3121      } else {
3122        commitHookEffectListMount(HookPassive | HookHasEffect, finishedWork);
3123      }
3124      break;
3125    }
3126    case Profiler: {
3127      commitProfilerPassiveEffect(finishedRoot, finishedWork);
3128      break;
3129    }
3130  }
3131}
3132
3133function invokeLayoutEffectMountInDEV(fiber: Fiber): void {
3134  if (__DEV__ && enableDoubleInvokingEffects) {
3135    // We don't need to re-check for legacy roots here.
3136    // This function will not be called within legacy roots.
3137    switch (fiber.tag) {
3138      case FunctionComponent:
3139      case ForwardRef:
3140      case SimpleMemoComponent: {
3141        invokeGuardedCallback(
3142          null,
3143          commitHookEffectListMount,
3144          null,
3145          HookLayout | HookHasEffect,
3146          fiber,
3147        );
3148        if (hasCaughtError()) {
3149          const mountError = clearCaughtError();
3150          captureCommitPhaseError(fiber, fiber.return, mountError);
3151        }
3152        break;
3153      }
3154      case ClassComponent: {
3155        const instance = fiber.stateNode;
3156        invokeGuardedCallback(null, instance.componentDidMount, instance);
3157        if (hasCaughtError()) {
3158          const mountError = clearCaughtError();
3159          captureCommitPhaseError(fiber, fiber.return, mountError);
3160        }
3161        break;
3162      }
3163    }
3164  }
3165}
3166
3167function invokePassiveEffectMountInDEV(fiber: Fiber): void {
3168  if (__DEV__ && enableDoubleInvokingEffects) {
3169    // We don't need to re-check for legacy roots here.
3170    // This function will not be called within legacy roots.
3171    switch (fiber.tag) {
3172      case FunctionComponent:
3173      case ForwardRef:
3174      case SimpleMemoComponent: {
3175        invokeGuardedCallback(
3176          null,
3177          commitHookEffectListMount,
3178          null,
3179          HookPassive | HookHasEffect,
3180          fiber,
3181        );
3182        if (hasCaughtError()) {
3183          const mountError = clearCaughtError();
3184          captureCommitPhaseError(fiber, fiber.return, mountError);
3185        }
3186        break;
3187      }
3188    }
3189  }
3190}
3191
3192function invokeLayoutEffectUnmountInDEV(fiber: Fiber): void {
3193  if (__DEV__ && enableDoubleInvokingEffects) {
3194    // We don't need to re-check for legacy roots here.
3195    // This function will not be called within legacy roots.
3196    switch (fiber.tag) {
3197      case FunctionComponent:
3198      case ForwardRef:
3199      case SimpleMemoComponent: {
3200        invokeGuardedCallback(
3201          null,
3202          commitHookEffectListUnmount,
3203          null,
3204          HookLayout | HookHasEffect,
3205          fiber,
3206          fiber.return,
3207        );
3208        if (hasCaughtError()) {
3209          const unmountError = clearCaughtError();
3210          captureCommitPhaseError(fiber, fiber.return, unmountError);
3211        }
3212        break;
3213      }
3214      case ClassComponent: {
3215        const instance = fiber.stateNode;
3216        if (typeof instance.componentWillUnmount === 'function') {
3217          safelyCallComponentWillUnmount(fiber, instance, fiber.return);
3218        }
3219        break;
3220      }
3221    }
3222  }
3223}
3224
3225function invokePassiveEffectUnmountInDEV(fiber: Fiber): void {
3226  if (__DEV__ && enableDoubleInvokingEffects) {
3227    // We don't need to re-check for legacy roots here.
3228    // This function will not be called within legacy roots.
3229    switch (fiber.tag) {
3230      case FunctionComponent:
3231      case ForwardRef:
3232      case SimpleMemoComponent: {
3233        invokeGuardedCallback(
3234          null,
3235          commitHookEffectListUnmount,
3236          null,
3237          HookPassive | HookHasEffect,
3238          fiber,
3239          fiber.return,
3240        );
3241        if (hasCaughtError()) {
3242          const unmountError = clearCaughtError();
3243          captureCommitPhaseError(fiber, fiber.return, unmountError);
3244        }
3245        break;
3246      }
3247    }
3248  }
3249}
3250
3251// TODO: Convert this to iteration instead of recursion, too. Leaving this for
3252// a follow up because the flag is off.
3253export function commitDoubleInvokeEffectsInDEV(
3254  fiber: Fiber,
3255  hasPassiveEffects: boolean,
3256) {
3257  if (__DEV__ && enableDoubleInvokingEffects) {
3258    // Never double-invoke effects for legacy roots.
3259    if ((fiber.mode & (BlockingMode | ConcurrentMode)) === NoMode) {
3260      return;
3261    }
3262
3263    setCurrentDebugFiberInDEV(fiber);
3264    invokeEffectsInDev(fiber, MountLayoutDev, invokeLayoutEffectUnmountInDEV);
3265    if (hasPassiveEffects) {
3266      invokeEffectsInDev(
3267        fiber,
3268        MountPassiveDev,
3269        invokePassiveEffectUnmountInDEV,
3270      );
3271    }
3272
3273    invokeEffectsInDev(fiber, MountLayoutDev, invokeLayoutEffectMountInDEV);
3274    if (hasPassiveEffects) {
3275      invokeEffectsInDev(fiber, MountPassiveDev, invokePassiveEffectMountInDEV);
3276    }
3277    resetCurrentDebugFiberInDEV();
3278  }
3279}
3280
3281function invokeEffectsInDev(
3282  firstChild: Fiber,
3283  fiberFlags: Flags,
3284  invokeEffectFn: (fiber: Fiber) => void,
3285): void {
3286  if (__DEV__ && enableDoubleInvokingEffects) {
3287    // We don't need to re-check for legacy roots here.
3288    // This function will not be called within legacy roots.
3289    let fiber = firstChild;
3290    while (fiber !== null) {
3291      if (fiber.child !== null) {
3292        const primarySubtreeFlag = fiber.subtreeFlags & fiberFlags;
3293        if (primarySubtreeFlag !== NoFlags) {
3294          invokeEffectsInDev(fiber.child, fiberFlags, invokeEffectFn);
3295        }