[Fiber] Extract Functions that Call User Space and Host Configs in Commit to Separate Modules (#30881)
This is mostly just moves and same code extracted into utility functions. This is to help clarify what needs to be wrapped in try/catch and runWithFiberInDEV. I'll do the runWithFiberInDEV changes in a follow up. This leaves ReactCommitWork mostly to do matching on the tag and the recursive loops.
Sebastian Markbåge committed
Sep 5, 2024 at 20:53 UTC
fe03c56d1e51379a18676b04cf185e76f04cd457
3 files changed
+1459
-1220
packages/react-reconciler/src/ReactFiberCommitEffects.js
new
+816
@@ -0,0 +1,816 @@
1
+/**
2
+ * Copyright (c) Meta Platforms, Inc. and 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
+
10
+import type {Fiber} from './ReactInternalTypes';
11
+import type {UpdateQueue} from './ReactFiberClassUpdateQueue';
12
+import type {FunctionComponentUpdateQueue} from './ReactFiberHooks';
13
+import type {HookFlags} from './ReactHookEffectTags';
14
+
15
+import {
16
+ enableProfilerTimer,
17
+ enableProfilerCommitHooks,
18
+ enableProfilerNestedUpdatePhase,
19
+ enableSchedulingProfiler,
20
+ enableScopeAPI,
21
+ disableStringRefs,
22
+} from 'shared/ReactFeatureFlags';
23
+import {
24
+ ClassComponent,
25
+ HostComponent,
26
+ HostHoistable,
27
+ HostSingleton,
28
+ ScopeComponent,
29
+} from './ReactWorkTags';
30
+import {NoFlags} from './ReactFiberFlags';
31
+import getComponentNameFromFiber from 'react-reconciler/src/getComponentNameFromFiber';
32
+import {resolveClassComponentProps} from './ReactFiberClassComponent';
33
+import {
34
+ recordLayoutEffectDuration,
35
+ startLayoutEffectTimer,
36
+ recordPassiveEffectDuration,
37
+ startPassiveEffectTimer,
38
+ isCurrentUpdateNested,
39
+} from './ReactProfilerTimer';
40
+import {NoMode, ProfileMode} from './ReactTypeOfMode';
41
+import {
42
+ commitCallbacks,
43
+ commitHiddenCallbacks,
44
+} from './ReactFiberClassUpdateQueue';
45
+import {getPublicInstance} from './ReactFiberConfig';
46
+import {
47
+ captureCommitPhaseError,
48
+ setIsRunningInsertionEffect,
49
+ getExecutionContext,
50
+ CommitContext,
51
+ NoContext,
52
+} from './ReactFiberWorkLoop';
53
+import {
54
+ NoFlags as NoHookEffect,
55
+ Layout as HookLayout,
56
+ Insertion as HookInsertion,
57
+ Passive as HookPassive,
58
+} from './ReactHookEffectTags';
59
+import {didWarnAboutReassigningProps} from './ReactFiberBeginWork';
60
+import {
61
+ markComponentPassiveEffectMountStarted,
62
+ markComponentPassiveEffectMountStopped,
63
+ markComponentPassiveEffectUnmountStarted,
64
+ markComponentPassiveEffectUnmountStopped,
65
+ markComponentLayoutEffectMountStarted,
66
+ markComponentLayoutEffectMountStopped,
67
+ markComponentLayoutEffectUnmountStarted,
68
+ markComponentLayoutEffectUnmountStopped,
69
+} from './ReactFiberDevToolsHook';
70
+import {
71
+ callComponentDidMountInDEV,
72
+ callComponentDidUpdateInDEV,
73
+ callComponentWillUnmountInDEV,
74
+ callCreateInDEV,
75
+ callDestroyInDEV,
76
+} from './ReactFiberCallUserSpace';
77
+
78
+function shouldProfile(current: Fiber): boolean {
79
+ return (
80
+ enableProfilerTimer &&
81
+ enableProfilerCommitHooks &&
82
+ (current.mode & ProfileMode) !== NoMode &&
83
+ (getExecutionContext() & CommitContext) !== NoContext
84
+ );
85
+}
86
+
87
+export function commitHookLayoutEffects(
88
+ finishedWork: Fiber,
89
+ hookFlags: HookFlags,
90
+) {
91
+ // At this point layout effects have already been destroyed (during mutation phase).
92
+ // This is done to prevent sibling component effects from interfering with each other,
93
+ // e.g. a destroy function in one component should never override a ref set
94
+ // by a create function in another component during the same commit.
95
+ if (shouldProfile(finishedWork)) {
96
+ startLayoutEffectTimer();
97
+ commitHookEffectListMount(hookFlags, finishedWork);
98
+ recordLayoutEffectDuration(finishedWork);
99
+ } else {
100
+ commitHookEffectListMount(hookFlags, finishedWork);
101
+ }
102
+}
103
+
104
+export function commitHookEffectListMount(
105
+ flags: HookFlags,
106
+ finishedWork: Fiber,
107
+) {
108
+ try {
109
+ const updateQueue: FunctionComponentUpdateQueue | null =
110
+ (finishedWork.updateQueue: any);
111
+ const lastEffect = updateQueue !== null ? updateQueue.lastEffect : null;
112
+ if (lastEffect !== null) {
113
+ const firstEffect = lastEffect.next;
114
+ let effect = firstEffect;
115
+ do {
116
+ if ((effect.tag & flags) === flags) {
117
+ if (enableSchedulingProfiler) {
118
+ if ((flags & HookPassive) !== NoHookEffect) {
119
+ markComponentPassiveEffectMountStarted(finishedWork);
120
+ } else if ((flags & HookLayout) !== NoHookEffect) {
121
+ markComponentLayoutEffectMountStarted(finishedWork);
122
+ }
123
+ }
124
+
125
+ // Mount
126
+ let destroy;
127
+ if (__DEV__) {
128
+ if ((flags & HookInsertion) !== NoHookEffect) {
129
+ setIsRunningInsertionEffect(true);
130
+ }
131
+ destroy = callCreateInDEV(effect);
132
+ if ((flags & HookInsertion) !== NoHookEffect) {
133
+ setIsRunningInsertionEffect(false);
134
+ }
135
+ } else {
136
+ const create = effect.create;
137
+ const inst = effect.inst;
138
+ destroy = create();
139
+ inst.destroy = destroy;
140
+ }
141
+
142
+ if (enableSchedulingProfiler) {
143
+ if ((flags & HookPassive) !== NoHookEffect) {
144
+ markComponentPassiveEffectMountStopped();
145
+ } else if ((flags & HookLayout) !== NoHookEffect) {
146
+ markComponentLayoutEffectMountStopped();
147
+ }
148
+ }
149
+
150
+ if (__DEV__) {
151
+ if (destroy !== undefined && typeof destroy !== 'function') {
152
+ let hookName;
153
+ if ((effect.tag & HookLayout) !== NoFlags) {
154
+ hookName = 'useLayoutEffect';
155
+ } else if ((effect.tag & HookInsertion) !== NoFlags) {
156
+ hookName = 'useInsertionEffect';
157
+ } else {
158
+ hookName = 'useEffect';
159
+ }
160
+ let addendum;
161
+ if (destroy === null) {
162
+ addendum =
163
+ ' You returned null. If your effect does not require clean ' +
164
+ 'up, return undefined (or nothing).';
165
+ } else if (typeof destroy.then === 'function') {
166
+ addendum =
167
+ '\n\nIt looks like you wrote ' +
168
+ hookName +
169
+ '(async () => ...) or returned a Promise. ' +
170
+ 'Instead, write the async function inside your effect ' +
171
+ 'and call it immediately:\n\n' +
172
+ hookName +
173
+ '(() => {\n' +
174
+ ' async function fetchData() {\n' +
175
+ ' // You can await here\n' +
176
+ ' const response = await MyAPI.getData(someId);\n' +
177
+ ' // ...\n' +
178
+ ' }\n' +
179
+ ' fetchData();\n' +
180
+ `}, [someId]); // Or [] if effect doesn't need props or state\n\n` +
181
+ 'Learn more about data fetching with Hooks: https://react.dev/link/hooks-data-fetching';
182
+ } else {
183
+ addendum = ' You returned: ' + destroy;
184
+ }
185
+ console.error(
186
+ '%s must not return anything besides a function, ' +
187
+ 'which is used for clean-up.%s',
188
+ hookName,
189
+ addendum,
190
+ );
191
+ }
192
+ }
193
+ }
194
+ effect = effect.next;
195
+ } while (effect !== firstEffect);
196
+ }
197
+ } catch (error) {
198
+ captureCommitPhaseError(finishedWork, finishedWork.return, error);
199
+ }
200
+}
201
+
202
+export function commitHookEffectListUnmount(
203
+ flags: HookFlags,
204
+ finishedWork: Fiber,
205
+ nearestMountedAncestor: Fiber | null,
206
+) {
207
+ try {
208
+ const updateQueue: FunctionComponentUpdateQueue | null =
209
+ (finishedWork.updateQueue: any);
210
+ const lastEffect = updateQueue !== null ? updateQueue.lastEffect : null;
211
+ if (lastEffect !== null) {
212
+ const firstEffect = lastEffect.next;
213
+ let effect = firstEffect;
214
+ do {
215
+ if ((effect.tag & flags) === flags) {
216
+ // Unmount
217
+ const inst = effect.inst;
218
+ const destroy = inst.destroy;
219
+ if (destroy !== undefined) {
220
+ inst.destroy = undefined;
221
+ if (enableSchedulingProfiler) {
222
+ if ((flags & HookPassive) !== NoHookEffect) {
223
+ markComponentPassiveEffectUnmountStarted(finishedWork);
224
+ } else if ((flags & HookLayout) !== NoHookEffect) {
225
+ markComponentLayoutEffectUnmountStarted(finishedWork);
226
+ }
227
+ }
228
+
229
+ if (__DEV__) {
230
+ if ((flags & HookInsertion) !== NoHookEffect) {
231
+ setIsRunningInsertionEffect(true);
232
+ }
233
+ }
234
+ safelyCallDestroy(finishedWork, nearestMountedAncestor, destroy);
235
+ if (__DEV__) {
236
+ if ((flags & HookInsertion) !== NoHookEffect) {
237
+ setIsRunningInsertionEffect(false);
238
+ }
239
+ }
240
+
241
+ if (enableSchedulingProfiler) {
242
+ if ((flags & HookPassive) !== NoHookEffect) {
243
+ markComponentPassiveEffectUnmountStopped();
244
+ } else if ((flags & HookLayout) !== NoHookEffect) {
245
+ markComponentLayoutEffectUnmountStopped();
246
+ }
247
+ }
248
+ }
249
+ }
250
+ effect = effect.next;
251
+ } while (effect !== firstEffect);
252
+ }
253
+ } catch (error) {
254
+ captureCommitPhaseError(finishedWork, finishedWork.return, error);
255
+ }
256
+}
257
+
258
+export function commitHookPassiveMountEffects(
259
+ finishedWork: Fiber,
260
+ hookFlags: HookFlags,
261
+) {
262
+ if (shouldProfile(finishedWork)) {
263
+ startPassiveEffectTimer();
264
+ commitHookEffectListMount(hookFlags, finishedWork);
265
+ recordPassiveEffectDuration(finishedWork);
266
+ } else {
267
+ commitHookEffectListMount(hookFlags, finishedWork);
268
+ }
269
+}
270
+
271
+export function commitHookPassiveUnmountEffects(
272
+ finishedWork: Fiber,
273
+ nearestMountedAncestor: null | Fiber,
274
+ hookFlags: HookFlags,
275
+) {
276
+ if (shouldProfile(finishedWork)) {
277
+ startPassiveEffectTimer();
278
+ commitHookEffectListUnmount(
279
+ hookFlags,
280
+ finishedWork,
281
+ nearestMountedAncestor,
282
+ );
283
+ recordPassiveEffectDuration(finishedWork);
284
+ } else {
285
+ commitHookEffectListUnmount(
286
+ hookFlags,
287
+ finishedWork,
288
+ nearestMountedAncestor,
289
+ );
290
+ }
291
+}
292
+
293
+export function commitClassLayoutLifecycles(
294
+ finishedWork: Fiber,
295
+ current: Fiber | null,
296
+) {
297
+ const instance = finishedWork.stateNode;
298
+ if (current === null) {
299
+ // We could update instance props and state here,
300
+ // but instead we rely on them being set during last render.
301
+ // TODO: revisit this when we implement resuming.
302
+ if (__DEV__) {
303
+ if (
304
+ !finishedWork.type.defaultProps &&
305
+ !('ref' in finishedWork.memoizedProps) &&
306
+ !didWarnAboutReassigningProps
307
+ ) {
308
+ if (instance.props !== finishedWork.memoizedProps) {
309
+ console.error(
310
+ 'Expected %s props to match memoized props before ' +
311
+ 'componentDidMount. ' +
312
+ 'This might either be because of a bug in React, or because ' +
313
+ 'a component reassigns its own `this.props`. ' +
314
+ 'Please file an issue.',
315
+ getComponentNameFromFiber(finishedWork) || 'instance',
316
+ );
317
+ }
318
+ if (instance.state !== finishedWork.memoizedState) {
319
+ console.error(
320
+ 'Expected %s state to match memoized state before ' +
321
+ 'componentDidMount. ' +
322
+ 'This might either be because of a bug in React, or because ' +
323
+ 'a component reassigns its own `this.state`. ' +
324
+ 'Please file an issue.',
325
+ getComponentNameFromFiber(finishedWork) || 'instance',
326
+ );
327
+ }
328
+ }
329
+ }
330
+ if (shouldProfile(finishedWork)) {
331
+ startLayoutEffectTimer();
332
+ if (__DEV__) {
333
+ callComponentDidMountInDEV(finishedWork, instance);
334
+ } else {
335
+ try {
336
+ instance.componentDidMount();
337
+ } catch (error) {
338
+ captureCommitPhaseError(finishedWork, finishedWork.return, error);
339
+ }
340
+ }
341
+ recordLayoutEffectDuration(finishedWork);
342
+ } else {
343
+ if (__DEV__) {
344
+ callComponentDidMountInDEV(finishedWork, instance);
345
+ } else {
346
+ try {
347
+ instance.componentDidMount();
348
+ } catch (error) {
349
+ captureCommitPhaseError(finishedWork, finishedWork.return, error);
350
+ }
351
+ }
352
+ }
353
+ } else {
354
+ const prevProps = resolveClassComponentProps(
355
+ finishedWork.type,
356
+ current.memoizedProps,
357
+ finishedWork.elementType === finishedWork.type,
358
+ );
359
+ const prevState = current.memoizedState;
360
+ // We could update instance props and state here,
361
+ // but instead we rely on them being set during last render.
362
+ // TODO: revisit this when we implement resuming.
363
+ if (__DEV__) {
364
+ if (
365
+ !finishedWork.type.defaultProps &&
366
+ !('ref' in finishedWork.memoizedProps) &&
367
+ !didWarnAboutReassigningProps
368
+ ) {
369
+ if (instance.props !== finishedWork.memoizedProps) {
370
+ console.error(
371
+ 'Expected %s props to match memoized props before ' +
372
+ 'componentDidUpdate. ' +
373
+ 'This might either be because of a bug in React, or because ' +
374
+ 'a component reassigns its own `this.props`. ' +
375
+ 'Please file an issue.',
376
+ getComponentNameFromFiber(finishedWork) || 'instance',
377
+ );
378
+ }
379
+ if (instance.state !== finishedWork.memoizedState) {
380
+ console.error(
381
+ 'Expected %s state to match memoized state before ' +
382
+ 'componentDidUpdate. ' +
383
+ 'This might either be because of a bug in React, or because ' +
384
+ 'a component reassigns its own `this.state`. ' +
385
+ 'Please file an issue.',
386
+ getComponentNameFromFiber(finishedWork) || 'instance',
387
+ );
388
+ }
389
+ }
390
+ }
391
+ if (shouldProfile(finishedWork)) {
392
+ startLayoutEffectTimer();
393
+ if (__DEV__) {
394
+ callComponentDidUpdateInDEV(
395
+ finishedWork,
396
+ instance,
397
+ prevProps,
398
+ prevState,
399
+ instance.__reactInternalSnapshotBeforeUpdate,
400
+ );
401
+ } else {
402
+ try {
403
+ instance.componentDidUpdate(
404
+ prevProps,
405
+ prevState,
406
+ instance.__reactInternalSnapshotBeforeUpdate,
407
+ );
408
+ } catch (error) {
409
+ captureCommitPhaseError(finishedWork, finishedWork.return, error);
410
+ }
411
+ }
412
+ recordLayoutEffectDuration(finishedWork);
413
+ } else {
414
+ if (__DEV__) {
415
+ callComponentDidUpdateInDEV(
416
+ finishedWork,
417
+ instance,
418
+ prevProps,
419
+ prevState,
420
+ instance.__reactInternalSnapshotBeforeUpdate,
421
+ );
422
+ } else {
423
+ try {
424
+ instance.componentDidUpdate(
425
+ prevProps,
426
+ prevState,
427
+ instance.__reactInternalSnapshotBeforeUpdate,
428
+ );
429
+ } catch (error) {
430
+ captureCommitPhaseError(finishedWork, finishedWork.return, error);
431
+ }
432
+ }
433
+ }
434
+ }
435
+}
436
+
437
+export function commitClassDidMount(finishedWork: Fiber) {
438
+ // TODO: Check for LayoutStatic flag
439
+ const instance = finishedWork.stateNode;
440
+ if (typeof instance.componentDidMount === 'function') {
441
+ if (__DEV__) {
442
+ callComponentDidMountInDEV(finishedWork, instance);
443
+ } else {
444
+ try {
445
+ instance.componentDidMount();
446
+ } catch (error) {
447
+ captureCommitPhaseError(finishedWork, finishedWork.return, error);
448
+ }
449
+ }
450
+ }
451
+}
452
+
453
+export function commitClassCallbacks(finishedWork: Fiber) {
454
+ // TODO: I think this is now always non-null by the time it reaches the
455
+ // commit phase. Consider removing the type check.
456
+ const updateQueue: UpdateQueue<mixed> | null =
457
+ (finishedWork.updateQueue: any);
458
+ if (updateQueue !== null) {
459
+ const instance = finishedWork.stateNode;
460
+ if (__DEV__) {
461
+ if (
462
+ !finishedWork.type.defaultProps &&
463
+ !('ref' in finishedWork.memoizedProps) &&
464
+ !didWarnAboutReassigningProps
465
+ ) {
466
+ if (instance.props !== finishedWork.memoizedProps) {
467
+ console.error(
468
+ 'Expected %s props to match memoized props before ' +
469
+ 'processing the update queue. ' +
470
+ 'This might either be because of a bug in React, or because ' +
471
+ 'a component reassigns its own `this.props`. ' +
472
+ 'Please file an issue.',
473
+ getComponentNameFromFiber(finishedWork) || 'instance',
474
+ );
475
+ }
476
+ if (instance.state !== finishedWork.memoizedState) {
477
+ console.error(
478
+ 'Expected %s state to match memoized state before ' +
479
+ 'processing the update queue. ' +
480
+ 'This might either be because of a bug in React, or because ' +
481
+ 'a component reassigns its own `this.state`. ' +
482
+ 'Please file an issue.',
483
+ getComponentNameFromFiber(finishedWork) || 'instance',
484
+ );
485
+ }
486
+ }
487
+ }
488
+ // We could update instance props and state here,
489
+ // but instead we rely on them being set during last render.
490
+ // TODO: revisit this when we implement resuming.
491
+ try {
492
+ commitCallbacks(updateQueue, instance);
493
+ } catch (error) {
494
+ captureCommitPhaseError(finishedWork, finishedWork.return, error);
495
+ }
496
+ }
497
+}
498
+
499
+export function commitClassHiddenCallbacks(finishedWork: Fiber) {
500
+ // Commit any callbacks that would have fired while the component
501
+ // was hidden.
502
+ const updateQueue: UpdateQueue<mixed> | null =
503
+ (finishedWork.updateQueue: any);
504
+ if (updateQueue !== null) {
505
+ const instance = finishedWork.stateNode;
506
+ try {
507
+ commitHiddenCallbacks(updateQueue, instance);
508
+ } catch (error) {
509
+ captureCommitPhaseError(finishedWork, finishedWork.return, error);
510
+ }
511
+ }
512
+}
513
+
514
+export function commitRootCallbacks(finishedWork: Fiber) {
515
+ // TODO: I think this is now always non-null by the time it reaches the
516
+ // commit phase. Consider removing the type check.
517
+ const updateQueue: UpdateQueue<mixed> | null =
518
+ (finishedWork.updateQueue: any);
519
+ if (updateQueue !== null) {
520
+ let instance = null;
521
+ if (finishedWork.child !== null) {
522
+ switch (finishedWork.child.tag) {
523
+ case HostSingleton:
524
+ case HostComponent:
525
+ instance = getPublicInstance(finishedWork.child.stateNode);
526
+ break;
527
+ case ClassComponent:
528
+ instance = finishedWork.child.stateNode;
529
+ break;
530
+ }
531
+ }
532
+ try {
533
+ commitCallbacks(updateQueue, instance);
534
+ } catch (error) {
535
+ captureCommitPhaseError(finishedWork, finishedWork.return, error);
536
+ }
537
+ }
538
+}
539
+
540
+let didWarnAboutUndefinedSnapshotBeforeUpdate: Set<mixed> | null = null;
541
+if (__DEV__) {
542
+ didWarnAboutUndefinedSnapshotBeforeUpdate = new Set();
543
+}
544
+
545
+export function commitClassSnapshot(finishedWork: Fiber, current: Fiber) {
546
+ const prevProps = current.memoizedProps;
547
+ const prevState = current.memoizedState;
548
+ const instance = finishedWork.stateNode;
549
+ // We could update instance props and state here,
550
+ // but instead we rely on them being set during last render.
551
+ // TODO: revisit this when we implement resuming.
552
+ if (__DEV__) {
553
+ if (
554
+ !finishedWork.type.defaultProps &&
555
+ !('ref' in finishedWork.memoizedProps) &&
556
+ !didWarnAboutReassigningProps
557
+ ) {
558
+ if (instance.props !== finishedWork.memoizedProps) {
559
+ console.error(
560
+ 'Expected %s props to match memoized props before ' +
561
+ 'getSnapshotBeforeUpdate. ' +
562
+ 'This might either be because of a bug in React, or because ' +
563
+ 'a component reassigns its own `this.props`. ' +
564
+ 'Please file an issue.',
565
+ getComponentNameFromFiber(finishedWork) || 'instance',
566
+ );
567
+ }
568
+ if (instance.state !== finishedWork.memoizedState) {
569
+ console.error(
570
+ 'Expected %s state to match memoized state before ' +
571
+ 'getSnapshotBeforeUpdate. ' +
572
+ 'This might either be because of a bug in React, or because ' +
573
+ 'a component reassigns its own `this.state`. ' +
574
+ 'Please file an issue.',
575
+ getComponentNameFromFiber(finishedWork) || 'instance',
576
+ );
577
+ }
578
+ }
579
+ }
580
+ try {
581
+ const snapshot = instance.getSnapshotBeforeUpdate(
582
+ resolveClassComponentProps(
583
+ finishedWork.type,
584
+ prevProps,
585
+ finishedWork.elementType === finishedWork.type,
586
+ ),
587
+ prevState,
588
+ );
589
+ if (__DEV__) {
590
+ const didWarnSet =
591
+ ((didWarnAboutUndefinedSnapshotBeforeUpdate: any): Set<mixed>);
592
+ if (snapshot === undefined && !didWarnSet.has(finishedWork.type)) {
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
+ getComponentNameFromFiber(finishedWork),
598
+ );
599
+ }
600
+ }
601
+ instance.__reactInternalSnapshotBeforeUpdate = snapshot;
602
+ } catch (error) {
603
+ captureCommitPhaseError(finishedWork, finishedWork.return, error);
604
+ }
605
+}
606
+
607
+// Capture errors so they don't interrupt unmounting.
608
+export function safelyCallComponentWillUnmount(
609
+ current: Fiber,
610
+ nearestMountedAncestor: Fiber | null,
611
+ instance: any,
612
+) {
613
+ instance.props = resolveClassComponentProps(
614
+ current.type,
615
+ current.memoizedProps,
616
+ current.elementType === current.type,
617
+ );
618
+ instance.state = current.memoizedState;
619
+ if (shouldProfile(current)) {
620
+ startLayoutEffectTimer();
621
+ if (__DEV__) {
622
+ callComponentWillUnmountInDEV(current, nearestMountedAncestor, instance);
623
+ } else {
624
+ try {
625
+ instance.componentWillUnmount();
626
+ } catch (error) {
627
+ captureCommitPhaseError(current, nearestMountedAncestor, error);
628
+ }
629
+ }
630
+ recordLayoutEffectDuration(current);
631
+ } else {
632
+ if (__DEV__) {
633
+ callComponentWillUnmountInDEV(current, nearestMountedAncestor, instance);
634
+ } else {
635
+ try {
636
+ instance.componentWillUnmount();
637
+ } catch (error) {
638
+ captureCommitPhaseError(current, nearestMountedAncestor, error);
639
+ }
640
+ }
641
+ }
642
+}
643
+
644
+function commitAttachRef(finishedWork: Fiber) {
645
+ const ref = finishedWork.ref;
646
+ if (ref !== null) {
647
+ const instance = finishedWork.stateNode;
648
+ let instanceToUse;
649
+ switch (finishedWork.tag) {
650
+ case HostHoistable:
651
+ case HostSingleton:
652
+ case HostComponent:
653
+ instanceToUse = getPublicInstance(instance);
654
+ break;
655
+ default:
656
+ instanceToUse = instance;
657
+ }
658
+ // Moved outside to ensure DCE works with this flag
659
+ if (enableScopeAPI && finishedWork.tag === ScopeComponent) {
660
+ instanceToUse = instance;
661
+ }
662
+ if (typeof ref === 'function') {
663
+ if (shouldProfile(finishedWork)) {
664
+ try {
665
+ startLayoutEffectTimer();
666
+ finishedWork.refCleanup = ref(instanceToUse);
667
+ } finally {
668
+ recordLayoutEffectDuration(finishedWork);
669
+ }
670
+ } else {
671
+ finishedWork.refCleanup = ref(instanceToUse);
672
+ }
673
+ } else {
674
+ if (__DEV__) {
675
+ // TODO: We should move these warnings to happen during the render
676
+ // phase (markRef).
677
+ if (disableStringRefs && typeof ref === 'string') {
678
+ console.error('String refs are no longer supported.');
679
+ } else if (!ref.hasOwnProperty('current')) {
680
+ console.error(
681
+ 'Unexpected ref object provided for %s. ' +
682
+ 'Use either a ref-setter function or React.createRef().',
683
+ getComponentNameFromFiber(finishedWork),
684
+ );
685
+ }
686
+ }
687
+
688
+ // $FlowFixMe[incompatible-use] unable to narrow type to the non-function case
689
+ ref.current = instanceToUse;
690
+ }
691
+ }
692
+}
693
+
694
+// Capture errors so they don't interrupt mounting.
695
+export function safelyAttachRef(
696
+ current: Fiber,
697
+ nearestMountedAncestor: Fiber | null,
698
+) {
699
+ try {
700
+ commitAttachRef(current);
701
+ } catch (error) {
702
+ captureCommitPhaseError(current, nearestMountedAncestor, error);
703
+ }
704
+}
705
+
706
+export function safelyDetachRef(
707
+ current: Fiber,
708
+ nearestMountedAncestor: Fiber | null,
709
+) {
710
+ const ref = current.ref;
711
+ const refCleanup = current.refCleanup;
712
+
713
+ if (ref !== null) {
714
+ if (typeof refCleanup === 'function') {
715
+ try {
716
+ if (shouldProfile(current)) {
717
+ try {
718
+ startLayoutEffectTimer();
719
+ refCleanup();
720
+ } finally {
721
+ recordLayoutEffectDuration(current);
722
+ }
723
+ } else {
724
+ refCleanup();
725
+ }
726
+ } catch (error) {
727
+ captureCommitPhaseError(current, nearestMountedAncestor, error);
728
+ } finally {
729
+ // `refCleanup` has been called. Nullify all references to it to prevent double invocation.
730
+ current.refCleanup = null;
731
+ const finishedWork = current.alternate;
732
+ if (finishedWork != null) {
733
+ finishedWork.refCleanup = null;
734
+ }
735
+ }
736
+ } else if (typeof ref === 'function') {
737
+ try {
738
+ if (shouldProfile(current)) {
739
+ try {
740
+ startLayoutEffectTimer();
741
+ ref(null);
742
+ } finally {
743
+ recordLayoutEffectDuration(current);
744
+ }
745
+ } else {
746
+ ref(null);
747
+ }
748
+ } catch (error) {
749
+ captureCommitPhaseError(current, nearestMountedAncestor, error);
750
+ }
751
+ } else {
752
+ // $FlowFixMe[incompatible-use] unable to narrow type to RefObject
753
+ ref.current = null;
754
+ }
755
+ }
756
+}
757
+
758
+export function safelyCallDestroy(
759
+ current: Fiber,
760
+ nearestMountedAncestor: Fiber | null,
761
+ destroy: () => void,
762
+) {
763
+ if (__DEV__) {
764
+ callDestroyInDEV(current, nearestMountedAncestor, destroy);
765
+ } else {
766
+ try {
767
+ destroy();
768
+ } catch (error) {
769
+ captureCommitPhaseError(current, nearestMountedAncestor, error);
770
+ }
771
+ }
772
+}
773
+
774
+export function commitProfilerUpdate(
775
+ finishedWork: Fiber,
776
+ current: Fiber | null,
777
+ commitTime: number,
778
+ effectDuration: number,
779
+) {
780
+ if (enableProfilerTimer && getExecutionContext() & CommitContext) {
781
+ try {
782
+ const {onCommit, onRender} = finishedWork.memoizedProps;
783
+
784
+ let phase = current === null ? 'mount' : 'update';
785
+ if (enableProfilerNestedUpdatePhase) {
786
+ if (isCurrentUpdateNested()) {
787
+ phase = 'nested-update';
788
+ }
789
+ }
790
+
791
+ if (typeof onRender === 'function') {
792
+ onRender(
793
+ finishedWork.memoizedProps.id,
794
+ phase,
795
+ finishedWork.actualDuration,
796
+ finishedWork.treeBaseDuration,
797
+ finishedWork.actualStartTime,
798
+ commitTime,
799
+ );
800
+ }
801
+
802
+ if (enableProfilerCommitHooks) {
803
+ if (typeof onCommit === 'function') {
804
+ onCommit(
805
+ finishedWork.memoizedProps.id,
806
+ phase,
807
+ effectDuration,
808
+ commitTime,
809
+ );
810
+ }
811
+ }
812
+ } catch (error) {
813
+ captureCommitPhaseError(finishedWork, finishedWork.return, error);
814
+ }
815
+ }
816
+}
packages/react-reconciler/src/ReactFiberCommitHostEffects.js
new
+430
@@ -0,0 +1,430 @@
1
+/**
2
+ * Copyright (c) Meta Platforms, Inc. and 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
+
10
+import type {
11
+ Instance,
12
+ TextInstance,
13
+ SuspenseInstance,
14
+ Container,
15
+ ChildSet,
16
+} from './ReactFiberConfig';
17
+import type {Fiber, FiberRoot} from './ReactInternalTypes';
18
+
19
+import {
20
+ HostRoot,
21
+ HostComponent,
22
+ HostHoistable,
23
+ HostSingleton,
24
+ HostText,
25
+ HostPortal,
26
+ DehydratedFragment,
27
+} from './ReactWorkTags';
28
+import {ContentReset, Placement} from './ReactFiberFlags';
29
+import {
30
+ supportsMutation,
31
+ supportsResources,
32
+ supportsSingletons,
33
+ commitMount,
34
+ commitUpdate,
35
+ resetTextContent,
36
+ commitTextUpdate,
37
+ appendChild,
38
+ appendChildToContainer,
39
+ insertBefore,
40
+ insertInContainerBefore,
41
+ replaceContainerChildren,
42
+ hideInstance,
43
+ hideTextInstance,
44
+ unhideInstance,
45
+ unhideTextInstance,
46
+ commitHydratedContainer,
47
+ commitHydratedSuspenseInstance,
48
+ removeChildFromContainer,
49
+ removeChild,
50
+ clearSingleton,
51
+ acquireSingletonInstance,
52
+} from './ReactFiberConfig';
53
+import {captureCommitPhaseError} from './ReactFiberWorkLoop';
54
+
55
+export function commitHostMount(finishedWork: Fiber) {
56
+ const type = finishedWork.type;
57
+ const props = finishedWork.memoizedProps;
58
+ const instance: Instance = finishedWork.stateNode;
59
+ try {
60
+ commitMount(instance, type, props, finishedWork);
61
+ } catch (error) {
62
+ captureCommitPhaseError(finishedWork, finishedWork.return, error);
63
+ }
64
+}
65
+
66
+export function commitHostUpdate(
67
+ finishedWork: Fiber,
68
+ newProps: any,
69
+ oldProps: any,
70
+) {
71
+ try {
72
+ commitUpdate(
73
+ finishedWork.stateNode,
74
+ finishedWork.type,
75
+ oldProps,
76
+ newProps,
77
+ finishedWork,
78
+ );
79
+ } catch (error) {
80
+ captureCommitPhaseError(finishedWork, finishedWork.return, error);
81
+ }
82
+}
83
+
84
+export function commitHostTextUpdate(
85
+ finishedWork: Fiber,
86
+ newText: string,
87
+ oldText: string,
88
+) {
89
+ const textInstance: TextInstance = finishedWork.stateNode;
90
+ try {
91
+ commitTextUpdate(textInstance, oldText, newText);
92
+ } catch (error) {
93
+ captureCommitPhaseError(finishedWork, finishedWork.return, error);
94
+ }
95
+}
96
+
97
+export function commitHostResetTextContent(finishedWork: Fiber) {
98
+ const instance: Instance = finishedWork.stateNode;
99
+ try {
100
+ resetTextContent(instance);
101
+ } catch (error) {
102
+ captureCommitPhaseError(finishedWork, finishedWork.return, error);
103
+ }
104
+}
105
+
106
+export function commitShowHideHostInstance(node: Fiber, isHidden: boolean) {
107
+ try {
108
+ const instance = node.stateNode;
109
+ if (isHidden) {
110
+ hideInstance(instance);
111
+ } else {
112
+ unhideInstance(node.stateNode, node.memoizedProps);
113
+ }
114
+ } catch (error) {
115
+ captureCommitPhaseError(node, node.return, error);
116
+ }
117
+}
118
+
119
+export function commitShowHideHostTextInstance(node: Fiber, isHidden: boolean) {
120
+ try {
121
+ const instance = node.stateNode;
122
+ if (isHidden) {
123
+ hideTextInstance(instance);
124
+ } else {
125
+ unhideTextInstance(instance, node.memoizedProps);
126
+ }
127
+ } catch (error) {
128
+ captureCommitPhaseError(node, node.return, error);
129
+ }
130
+}
131
+
132
+function getHostParentFiber(fiber: Fiber): Fiber {
133
+ let parent = fiber.return;
134
+ while (parent !== null) {
135
+ if (isHostParent(parent)) {
136
+ return parent;
137
+ }
138
+ parent = parent.return;
139
+ }
140
+
141
+ throw new Error(
142
+ 'Expected to find a host parent. This error is likely caused by a bug ' +
143
+ 'in React. Please file an issue.',
144
+ );
145
+}
146
+
147
+function isHostParent(fiber: Fiber): boolean {
148
+ return (
149
+ fiber.tag === HostComponent ||
150
+ fiber.tag === HostRoot ||
151
+ (supportsResources ? fiber.tag === HostHoistable : false) ||
152
+ (supportsSingletons ? fiber.tag === HostSingleton : false) ||
153
+ fiber.tag === HostPortal
154
+ );
155
+}
156
+
157
+function getHostSibling(fiber: Fiber): ?Instance {
158
+ // We're going to search forward into the tree until we find a sibling host
159
+ // node. Unfortunately, if multiple insertions are done in a row we have to
160
+ // search past them. This leads to exponential search for the next sibling.
161
+ // TODO: Find a more efficient way to do this.
162
+ let node: Fiber = fiber;
163
+ siblings: while (true) {
164
+ // If we didn't find anything, let's try the next sibling.
165
+ while (node.sibling === null) {
166
+ if (node.return === null || isHostParent(node.return)) {
167
+ // If we pop out of the root or hit the parent the fiber we are the
168
+ // last sibling.
169
+ return null;
170
+ }
171
+ // $FlowFixMe[incompatible-type] found when upgrading Flow
172
+ node = node.return;
173
+ }
174
+ node.sibling.return = node.return;
175
+ node = node.sibling;
176
+ while (
177
+ node.tag !== HostComponent &&
178
+ node.tag !== HostText &&
179
+ (!supportsSingletons ? true : node.tag !== HostSingleton) &&
180
+ node.tag !== DehydratedFragment
181
+ ) {
182
+ // If it is not host node and, we might have a host node inside it.
183
+ // Try to search down until we find one.
184
+ if (node.flags & Placement) {
185
+ // If we don't have a child, try the siblings instead.
186
+ continue siblings;
187
+ }
188
+ // If we don't have a child, try the siblings instead.
189
+ // We also skip portals because they are not part of this host tree.
190
+ if (node.child === null || node.tag === HostPortal) {
191
+ continue siblings;
192
+ } else {
193
+ node.child.return = node;
194
+ node = node.child;
195
+ }
196
+ }
197
+ // Check if this host node is stable or about to be placed.
198
+ if (!(node.flags & Placement)) {
199
+ // Found it!
200
+ return node.stateNode;
201
+ }
202
+ }
203
+}
204
+
205
+function insertOrAppendPlacementNodeIntoContainer(
206
+ node: Fiber,
207
+ before: ?Instance,
208
+ parent: Container,
209
+): void {
210
+ const {tag} = node;
211
+ const isHost = tag === HostComponent || tag === HostText;
212
+ if (isHost) {
213
+ const stateNode = node.stateNode;
214
+ if (before) {
215
+ insertInContainerBefore(parent, stateNode, before);
216
+ } else {
217
+ appendChildToContainer(parent, stateNode);
218
+ }
219
+ } else if (
220
+ tag === HostPortal ||
221
+ (supportsSingletons ? tag === HostSingleton : false)
222
+ ) {
223
+ // If the insertion itself is a portal, then we don't want to traverse
224
+ // down its children. Instead, we'll get insertions from each child in
225
+ // the portal directly.
226
+ // If the insertion is a HostSingleton then it will be placed independently
227
+ } else {
228
+ const child = node.child;
229
+ if (child !== null) {
230
+ insertOrAppendPlacementNodeIntoContainer(child, before, parent);
231
+ let sibling = child.sibling;
232
+ while (sibling !== null) {
233
+ insertOrAppendPlacementNodeIntoContainer(sibling, before, parent);
234
+ sibling = sibling.sibling;
235
+ }
236
+ }
237
+ }
238
+}
239
+
240
+function insertOrAppendPlacementNode(
241
+ node: Fiber,
242
+ before: ?Instance,
243
+ parent: Instance,
244
+): void {
245
+ const {tag} = node;
246
+ const isHost = tag === HostComponent || tag === HostText;
247
+ if (isHost) {
248
+ const stateNode = node.stateNode;
249
+ if (before) {
250
+ insertBefore(parent, stateNode, before);
251
+ } else {
252
+ appendChild(parent, stateNode);
253
+ }
254
+ } else if (
255
+ tag === HostPortal ||
256
+ (supportsSingletons ? tag === HostSingleton : false)
257
+ ) {
258
+ // If the insertion itself is a portal, then we don't want to traverse
259
+ // down its children. Instead, we'll get insertions from each child in
260
+ // the portal directly.
261
+ // If the insertion is a HostSingleton then it will be placed independently
262
+ } else {
263
+ const child = node.child;
264
+ if (child !== null) {
265
+ insertOrAppendPlacementNode(child, before, parent);
266
+ let sibling = child.sibling;
267
+ while (sibling !== null) {
268
+ insertOrAppendPlacementNode(sibling, before, parent);
269
+ sibling = sibling.sibling;
270
+ }
271
+ }
272
+ }
273
+}
274
+
275
+function commitPlacement(finishedWork: Fiber): void {
276
+ if (!supportsMutation) {
277
+ return;
278
+ }
279
+
280
+ if (supportsSingletons) {
281
+ if (finishedWork.tag === HostSingleton) {
282
+ // Singletons are already in the Host and don't need to be placed
283
+ // Since they operate somewhat like Portals though their children will
284
+ // have Placement and will get placed inside them
285
+ return;
286
+ }
287
+ }
288
+ // Recursively insert all host nodes into the parent.
289
+ const parentFiber = getHostParentFiber(finishedWork);
290
+
291
+ switch (parentFiber.tag) {
292
+ case HostSingleton: {
293
+ if (supportsSingletons) {
294
+ const parent: Instance = parentFiber.stateNode;
295
+ const before = getHostSibling(finishedWork);
296
+ // We only have the top Fiber that was inserted but we need to recurse down its
297
+ // children to find all the terminal nodes.
298
+ insertOrAppendPlacementNode(finishedWork, before, parent);
299
+ break;
300
+ }
301
+ // Fall through
302
+ }
303
+ case HostComponent: {
304
+ const parent: Instance = parentFiber.stateNode;
305
+ if (parentFiber.flags & ContentReset) {
306
+ // Reset the text content of the parent before doing any insertions
307
+ resetTextContent(parent);
308
+ // Clear ContentReset from the effect tag
309
+ parentFiber.flags &= ~ContentReset;
310
+ }
311
+
312
+ const before = getHostSibling(finishedWork);
313
+ // We only have the top Fiber that was inserted but we need to recurse down its
314
+ // children to find all the terminal nodes.
315
+ insertOrAppendPlacementNode(finishedWork, before, parent);
316
+ break;
317
+ }
318
+ case HostRoot:
319
+ case HostPortal: {
320
+ const parent: Container = parentFiber.stateNode.containerInfo;
321
+ const before = getHostSibling(finishedWork);
322
+ insertOrAppendPlacementNodeIntoContainer(finishedWork, before, parent);
323
+ break;
324
+ }
325
+ default:
326
+ throw new Error(
327
+ 'Invalid host parent fiber. This error is likely caused by a bug ' +
328
+ 'in React. Please file an issue.',
329
+ );
330
+ }
331
+}
332
+
333
+export function commitHostPlacement(finishedWork: Fiber) {
334
+ try {
335
+ commitPlacement(finishedWork);
336
+ } catch (error) {
337
+ captureCommitPhaseError(finishedWork, finishedWork.return, error);
338
+ }
339
+}
340
+
341
+export function commitHostRemoveChildFromContainer(
342
+ deletedFiber: Fiber,
343
+ nearestMountedAncestor: Fiber,
344
+ parentContainer: Container,
345
+ hostInstance: Instance | TextInstance,
346
+) {
347
+ try {
348
+ removeChildFromContainer(parentContainer, hostInstance);
349
+ } catch (error) {
350
+ captureCommitPhaseError(deletedFiber, nearestMountedAncestor, error);
351
+ }
352
+}
353
+
354
+export function commitHostRemoveChild(
355
+ deletedFiber: Fiber,
356
+ nearestMountedAncestor: Fiber,
357
+ parentInstance: Instance,
358
+ hostInstance: Instance | TextInstance,
359
+) {
360
+ try {
361
+ removeChild(parentInstance, hostInstance);
362
+ } catch (error) {
363
+ captureCommitPhaseError(deletedFiber, nearestMountedAncestor, error);
364
+ }
365
+}
366
+
367
+export function commitHostRootContainerChildren(
368
+ root: FiberRoot,
369
+ finishedWork: Fiber,
370
+) {
371
+ const containerInfo = root.containerInfo;
372
+ const pendingChildren = root.pendingChildren;
373
+ try {
374
+ replaceContainerChildren(containerInfo, pendingChildren);
375
+ } catch (error) {
376
+ captureCommitPhaseError(finishedWork, finishedWork.return, error);
377
+ }
378
+}
379
+
380
+export function commitHostPortalContainerChildren(
381
+ portal: {
382
+ containerInfo: Container,
383
+ pendingChildren: ChildSet,
384
+ ...
385
+ },
386
+ finishedWork: Fiber,
387
+ pendingChildren: ChildSet,
388
+) {
389
+ const containerInfo = portal.containerInfo;
390
+ try {
391
+ replaceContainerChildren(containerInfo, pendingChildren);
392
+ } catch (error) {
393
+ captureCommitPhaseError(finishedWork, finishedWork.return, error);
394
+ }
395
+}
396
+
397
+export function commitHostHydratedContainer(
398
+ root: FiberRoot,
399
+ finishedWork: Fiber,
400
+) {
401
+ try {
402
+ commitHydratedContainer(root.containerInfo);
403
+ } catch (error) {
404
+ captureCommitPhaseError(finishedWork, finishedWork.return, error);
405
+ }
406
+}
407
+
408
+export function commitHostHydratedSuspense(
409
+ suspenseInstance: SuspenseInstance,
410
+ finishedWork: Fiber,
411
+) {
412
+ try {
413
+ commitHydratedSuspenseInstance(suspenseInstance);
414
+ } catch (error) {
415
+ captureCommitPhaseError(finishedWork, finishedWork.return, error);
416
+ }
417
+}
418
+
419
+export function commitHostSingleton(finishedWork: Fiber) {
420
+ const singleton = finishedWork.stateNode;
421
+ const props = finishedWork.memoizedProps;
422
+
423
+ try {
424
+ // This was a new mount, we need to clear and set initial properties
425
+ clearSingleton(singleton);
426
+ acquireSingletonInstance(finishedWork.type, props, singleton, finishedWork);
427
+ } catch (error) {
428
+ captureCommitPhaseError(finishedWork, finishedWork.return, error);
429
+ }
430
+}
packages/react-reconciler/src/ReactFiberCommitWork.js
+213
-1220
@@ -12,7 +12,6 @@ import type {
12
TextInstance,
13
SuspenseInstance,
14
Container,
15
- ChildSet,
15
HoistableRoot,
16
FormInstance,
17
} from './ReactFiberConfig';
@@ -30,7 +29,6 @@ import type {
29
OffscreenQueue,
30
OffscreenProps,
31
} from './ReactFiberActivityComponent';
33
-import type {HookFlags} from './ReactHookEffectTags';
32
import type {Cache} from './ReactFiberCacheComponent';
33
import type {RootState} from './ReactFiberRoot';
34
import type {
@@ -54,7 +52,6 @@ import {
52
enableTransitionTracing,
53
enableUseEffectEventHook,
54
enableLegacyHidden,
57
- disableStringRefs,
55
disableLegacyMode,
56
} from 'shared/ReactFeatureFlags';
57
import {
@@ -101,57 +98,29 @@ import {
98
FormReset,
99
Cloned,
100
} from './ReactFiberFlags';
104
-import getComponentNameFromFiber from 'react-reconciler/src/getComponentNameFromFiber';
101
import {runWithFiberInDEV} from './ReactCurrentFiber';
106
-import {resolveClassComponentProps} from './ReactFiberClassComponent';
102
import {
103
isCurrentUpdateNested,
104
getCommitTime,
105
recordLayoutEffectDuration,
106
startLayoutEffectTimer,
112
- recordPassiveEffectDuration,
113
- startPassiveEffectTimer,
107
} from './ReactProfilerTimer';
108
import {ConcurrentMode, NoMode, ProfileMode} from './ReactTypeOfMode';
109
+import {deferHiddenCallbacks} from './ReactFiberClassUpdateQueue';
110
import {
117
- deferHiddenCallbacks,
118
- commitHiddenCallbacks,
119
- commitCallbacks,
120
-} from './ReactFiberClassUpdateQueue';
121
-import {
122
- getPublicInstance,
111
supportsMutation,
112
supportsPersistence,
113
supportsHydration,
114
supportsResources,
115
supportsSingletons,
128
- commitMount,
129
- commitUpdate,
130
- resetTextContent,
131
- commitTextUpdate,
132
- appendChild,
133
- appendChildToContainer,
134
- insertBefore,
135
- insertInContainerBefore,
136
- removeChild,
137
- removeChildFromContainer,
116
clearSuspenseBoundary,
117
clearSuspenseBoundaryFromContainer,
140
- replaceContainerChildren,
118
createContainerChildSet,
142
- hideInstance,
143
- hideTextInstance,
144
- unhideInstance,
145
- unhideTextInstance,
146
- commitHydratedContainer,
147
- commitHydratedSuspenseInstance,
119
clearContainer,
120
prepareScopeUpdate,
121
prepareForCommit,
122
beforeActiveInstanceBlur,
123
detachDeletedInstance,
153
- clearSingleton,
154
- acquireSingletonInstance,
124
releaseSingletonInstance,
125
getHoistableRoot,
126
acquireResource,
@@ -176,7 +145,6 @@ import {
145
addMarkerProgressCallbackToPendingTransition,
146
addMarkerIncompleteCallbackToPendingTransition,
147
addMarkerCompleteCallbackToPendingTransition,
179
- setIsRunningInsertionEffect,
148
getExecutionContext,
149
CommitContext,
150
NoContext,
@@ -188,16 +156,9 @@ import {
156
Insertion as HookInsertion,
157
Passive as HookPassive,
158
} from './ReactHookEffectTags';
191
-import {didWarnAboutReassigningProps} from './ReactFiberBeginWork';
159
import {doesFiberContain} from './ReactFiberTreeReflection';
160
import {
161
isDevToolsPresent,
195
- markComponentPassiveEffectMountStarted,
196
- markComponentPassiveEffectMountStopped,
197
- markComponentPassiveEffectUnmountStarted,
198
- markComponentPassiveEffectUnmountStopped,
199
- markComponentLayoutEffectMountStarted,
200
- markComponentLayoutEffectMountStopped,
162
markComponentLayoutEffectUnmountStarted,
163
markComponentLayoutEffectUnmountStopped,
164
onCommitUnmount,
@@ -216,17 +177,39 @@ import {
177
import {scheduleUpdateOnFiber} from './ReactFiberWorkLoop';
178
import {enqueueConcurrentRenderForLane} from './ReactFiberConcurrentUpdates';
179
import {
219
- callComponentDidMountInDEV,
220
- callComponentDidUpdateInDEV,
221
- callComponentWillUnmountInDEV,
222
- callCreateInDEV,
223
- callDestroyInDEV,
224
-} from './ReactFiberCallUserSpace';
225
-
226
-let didWarnAboutUndefinedSnapshotBeforeUpdate: Set<mixed> | null = null;
227
-if (__DEV__) {
228
- didWarnAboutUndefinedSnapshotBeforeUpdate = new Set();
229
-}
180
+ commitHookLayoutEffects,
181
+ commitHookEffectListMount,
182
+ commitHookEffectListUnmount,
183
+ commitHookPassiveMountEffects,
184
+ commitHookPassiveUnmountEffects,
185
+ commitClassLayoutLifecycles,
186
+ commitClassDidMount,
187
+ commitClassCallbacks,
188
+ commitClassHiddenCallbacks,
189
+ commitClassSnapshot,
190
+ safelyCallComponentWillUnmount,
191
+ safelyAttachRef,
192
+ safelyDetachRef,
193
+ safelyCallDestroy,
194
+ commitProfilerUpdate,
195
+ commitRootCallbacks,
196
+} from './ReactFiberCommitEffects';
197
+import {
198
+ commitHostMount,
199
+ commitHostUpdate,
200
+ commitHostTextUpdate,
201
+ commitHostResetTextContent,
202
+ commitShowHideHostInstance,
203
+ commitShowHideHostTextInstance,
204
+ commitHostPlacement,
205
+ commitHostRootContainerChildren,
206
+ commitHostPortalContainerChildren,
207
+ commitHostHydratedContainer,
208
+ commitHostHydratedSuspense,
209
+ commitHostRemoveChildFromContainer,
210
+ commitHostRemoveChild,
211
+ commitHostSingleton,
212
+} from './ReactFiberCommitHostEffects';
213
214
// Used during the commit phase to track the state of the Offscreen component stack.
215
// Allows us to avoid traversing the return path to find the nearest Offscreen ancestor.
@@ -253,117 +236,6 @@ function shouldProfile(current: Fiber): boolean {
236
);
237
}
238
256
-// Capture errors so they don't interrupt unmounting.
257
-function safelyCallComponentWillUnmount(
258
- current: Fiber,
259
- nearestMountedAncestor: Fiber | null,
260
- instance: any,
261
-) {
262
- instance.props = resolveClassComponentProps(
263
- current.type,
264
- current.memoizedProps,
265
- current.elementType === current.type,
266
- );
267
- instance.state = current.memoizedState;
268
- if (shouldProfile(current)) {
269
- startLayoutEffectTimer();
270
- if (__DEV__) {
271
- callComponentWillUnmountInDEV(current, nearestMountedAncestor, instance);
272
- } else {
273
- try {
274
- instance.componentWillUnmount();
275
- } catch (error) {
276
- captureCommitPhaseError(current, nearestMountedAncestor, error);
277
- }
278
- }
279
- recordLayoutEffectDuration(current);
280
- } else {
281
- if (__DEV__) {
282
- callComponentWillUnmountInDEV(current, nearestMountedAncestor, instance);
283
- } else {
284
- try {
285
- instance.componentWillUnmount();
286
- } catch (error) {
287
- captureCommitPhaseError(current, nearestMountedAncestor, error);
288
- }
289
- }
290
- }
291
-}
292
-
293
-// Capture errors so they don't interrupt mounting.
294
-function safelyAttachRef(current: Fiber, nearestMountedAncestor: Fiber | null) {
295
- try {
296
- commitAttachRef(current);
297
- } catch (error) {
298
- captureCommitPhaseError(current, nearestMountedAncestor, error);
299
- }
300
-}
301
-
302
-function safelyDetachRef(current: Fiber, nearestMountedAncestor: Fiber | null) {
303
- const ref = current.ref;
304
- const refCleanup = current.refCleanup;
305
-
306
- if (ref !== null) {
307
- if (typeof refCleanup === 'function') {
308
- try {
309
- if (shouldProfile(current)) {
310
- try {
311
- startLayoutEffectTimer();
312
- refCleanup();
313
- } finally {
314
- recordLayoutEffectDuration(current);
315
- }
316
- } else {
317
- refCleanup();
318
- }
319
- } catch (error) {
320
- captureCommitPhaseError(current, nearestMountedAncestor, error);
321
- } finally {
322
- // `refCleanup` has been called. Nullify all references to it to prevent double invocation.
323
- current.refCleanup = null;
324
- const finishedWork = current.alternate;
325
- if (finishedWork != null) {
326
- finishedWork.refCleanup = null;
327
- }
328
- }
329
- } else if (typeof ref === 'function') {
330
- try {
331
- if (shouldProfile(current)) {
332
- try {
333
- startLayoutEffectTimer();
334
- ref(null);
335
- } finally {
336
- recordLayoutEffectDuration(current);
337
- }
338
- } else {
339
- ref(null);
340
- }
341
- } catch (error) {
342
- captureCommitPhaseError(current, nearestMountedAncestor, error);
343
- }
344
- } else {
345
- // $FlowFixMe[incompatible-use] unable to narrow type to RefObject
346
- ref.current = null;
347
- }
348
- }
349
-}
350
-
351
-function safelyCallDestroy(
352
- current: Fiber,
353
- nearestMountedAncestor: Fiber | null,
354
- destroy: () => void,
355
-) {
356
- if (__DEV__) {
357
- callDestroyInDEV(current, nearestMountedAncestor, destroy);
358
- } else {
359
- try {
360
- destroy();
361
- } catch (error) {
362
- captureCommitPhaseError(current, nearestMountedAncestor, error);
363
- }
364
- }
365
-}
366
-
239
let focusedInstanceHandle: null | Fiber = null;
240
let shouldFireAfterActiveInstanceBlur: boolean = false;
241
@@ -417,14 +289,10 @@ function commitBeforeMutationEffects_begin() {
289
function commitBeforeMutationEffects_complete() {
290
while (nextEffect !== null) {
291
const fiber = nextEffect;
420
- try {
421
- if (__DEV__) {
422
- runWithFiberInDEV(fiber, commitBeforeMutationEffectsOnFiber, fiber);
423
- } else {
424
- commitBeforeMutationEffectsOnFiber(fiber);
425
- }
426
- } catch (error) {
427
- captureCommitPhaseError(fiber, fiber.return, error);
292
+ if (__DEV__) {
293
+ runWithFiberInDEV(fiber, commitBeforeMutationEffectsOnFiber, fiber);
294
+ } else {
295
+ commitBeforeMutationEffectsOnFiber(fiber);
296
}
297
298
const sibling = fiber.sibling;
@@ -462,7 +330,16 @@ function commitBeforeMutationEffectsOnFiber(finishedWork: Fiber) {
330
case FunctionComponent: {
331
if (enableUseEffectEventHook) {
332
if ((flags & Update) !== NoFlags) {
465
- commitUseEffectEventMount(finishedWork);
333
+ const updateQueue: FunctionComponentUpdateQueue | null =
334
+ (finishedWork.updateQueue: any);
335
+ const eventPayloads =
336
+ updateQueue !== null ? updateQueue.events : null;
337
+ if (eventPayloads !== null) {
338
+ for (let ii = 0; ii < eventPayloads.length; ii++) {
339
+ const {ref, nextImpl} = eventPayloads[ii];
340
+ ref.impl = nextImpl;
341
+ }
342
+ }
343
}
344
}
345
break;
@@ -474,61 +351,7 @@ function commitBeforeMutationEffectsOnFiber(finishedWork: Fiber) {
351
case ClassComponent: {
352
if ((flags & Snapshot) !== NoFlags) {
353
if (current !== null) {
477
- const prevProps = current.memoizedProps;
478
- const prevState = current.memoizedState;
479
- const instance = finishedWork.stateNode;
480
- // We could update instance props and state here,
481
- // but instead we rely on them being set during last render.
482
- // TODO: revisit this when we implement resuming.
483
- if (__DEV__) {
484
- if (
485
- !finishedWork.type.defaultProps &&
486
- !('ref' in finishedWork.memoizedProps) &&
487
- !didWarnAboutReassigningProps
488
- ) {
489
- if (instance.props !== finishedWork.memoizedProps) {
490
- console.error(
491
- 'Expected %s props to match memoized props before ' +
492
- 'getSnapshotBeforeUpdate. ' +
493
- 'This might either be because of a bug in React, or because ' +
494
- 'a component reassigns its own `this.props`. ' +
495
- 'Please file an issue.',
496
- getComponentNameFromFiber(finishedWork) || 'instance',
497
- );
498
- }
499
- if (instance.state !== finishedWork.memoizedState) {
500
- console.error(
501
- 'Expected %s state to match memoized state before ' +
502
- 'getSnapshotBeforeUpdate. ' +
503
- 'This might either be because of a bug in React, or because ' +
504
- 'a component reassigns its own `this.state`. ' +
505
- 'Please file an issue.',
506
- getComponentNameFromFiber(finishedWork) || 'instance',
507
- );
508
- }
509
- }
510
- }
511
- const snapshot = instance.getSnapshotBeforeUpdate(
512
- resolveClassComponentProps(
513
- finishedWork.type,
514
- prevProps,
515
- finishedWork.elementType === finishedWork.type,
516
- ),
517
- prevState,
518
- );
519
- if (__DEV__) {
520
- const didWarnSet =
521
- ((didWarnAboutUndefinedSnapshotBeforeUpdate: any): Set<mixed>);
522
- if (snapshot === undefined && !didWarnSet.has(finishedWork.type)) {
523
- didWarnSet.add(finishedWork.type);
524
- console.error(
525
- '%s.getSnapshotBeforeUpdate(): A snapshot value (or null) ' +
526
- 'must be returned. You have returned undefined.',
527
- getComponentNameFromFiber(finishedWork),
528
- );
529
- }
530
- }
531
- instance.__reactInternalSnapshotBeforeUpdate = snapshot;
354
+ commitClassSnapshot(finishedWork, current);
355
}
356
}
357
break;
@@ -574,161 +397,6 @@ function commitBeforeMutationEffectsDeletion(deletion: Fiber) {
397
}
398
}
399
577
-function commitHookEffectListUnmount(
578
- flags: HookFlags,
579
- finishedWork: Fiber,
580
- nearestMountedAncestor: Fiber | null,
581
-) {
582
- const updateQueue: FunctionComponentUpdateQueue | null =
583
- (finishedWork.updateQueue: any);
584
- const lastEffect = updateQueue !== null ? updateQueue.lastEffect : null;
585
- if (lastEffect !== null) {
586
- const firstEffect = lastEffect.next;
587
- let effect = firstEffect;
588
- do {
589
- if ((effect.tag & flags) === flags) {
590
- // Unmount
591
- const inst = effect.inst;
592
- const destroy = inst.destroy;
593
- if (destroy !== undefined) {
594
- inst.destroy = undefined;
595
- if (enableSchedulingProfiler) {
596
- if ((flags & HookPassive) !== NoHookEffect) {
597
- markComponentPassiveEffectUnmountStarted(finishedWork);
598
- } else if ((flags & HookLayout) !== NoHookEffect) {
599
- markComponentLayoutEffectUnmountStarted(finishedWork);
600
- }
601
- }
602
-
603
- if (__DEV__) {
604
- if ((flags & HookInsertion) !== NoHookEffect) {
605
- setIsRunningInsertionEffect(true);
606
- }
607
- }
608
- safelyCallDestroy(finishedWork, nearestMountedAncestor, destroy);
609
- if (__DEV__) {
610
- if ((flags & HookInsertion) !== NoHookEffect) {
611
- setIsRunningInsertionEffect(false);
612
- }
613
- }
614
-
615
- if (enableSchedulingProfiler) {
616
- if ((flags & HookPassive) !== NoHookEffect) {
617
- markComponentPassiveEffectUnmountStopped();
618
- } else if ((flags & HookLayout) !== NoHookEffect) {
619
- markComponentLayoutEffectUnmountStopped();
620
- }
621
- }
622
- }
623
- }
624
- effect = effect.next;
625
- } while (effect !== firstEffect);
626
- }
627
-}
628
-
629
-function commitHookEffectListMount(flags: HookFlags, finishedWork: Fiber) {
630
- const updateQueue: FunctionComponentUpdateQueue | null =
631
- (finishedWork.updateQueue: any);
632
- const lastEffect = updateQueue !== null ? updateQueue.lastEffect : null;
633
- if (lastEffect !== null) {
634
- const firstEffect = lastEffect.next;
635
- let effect = firstEffect;
636
- do {
637
- if ((effect.tag & flags) === flags) {
638
- if (enableSchedulingProfiler) {
639
- if ((flags & HookPassive) !== NoHookEffect) {
640
- markComponentPassiveEffectMountStarted(finishedWork);
641
- } else if ((flags & HookLayout) !== NoHookEffect) {
642
- markComponentLayoutEffectMountStarted(finishedWork);
643
- }
644
- }
645
-
646
- // Mount
647
- let destroy;
648
- if (__DEV__) {
649
- if ((flags & HookInsertion) !== NoHookEffect) {
650
- setIsRunningInsertionEffect(true);
651
- }
652
- destroy = callCreateInDEV(effect);
653
- if ((flags & HookInsertion) !== NoHookEffect) {
654
- setIsRunningInsertionEffect(false);
655
- }
656
- } else {
657
- const create = effect.create;
658
- const inst = effect.inst;
659
- destroy = create();
660
- inst.destroy = destroy;
661
- }
662
-
663
- if (enableSchedulingProfiler) {
664
- if ((flags & HookPassive) !== NoHookEffect) {
665
- markComponentPassiveEffectMountStopped();
666
- } else if ((flags & HookLayout) !== NoHookEffect) {
667
- markComponentLayoutEffectMountStopped();
668
- }
669
- }
670
-
671
- if (__DEV__) {
672
- if (destroy !== undefined && typeof destroy !== 'function') {
673
- let hookName;
674
- if ((effect.tag & HookLayout) !== NoFlags) {
675
- hookName = 'useLayoutEffect';
676
- } else if ((effect.tag & HookInsertion) !== NoFlags) {
677
- hookName = 'useInsertionEffect';
678
- } else {
679
- hookName = 'useEffect';
680
- }
681
- let addendum;
682
- if (destroy === null) {
683
- addendum =
684
- ' You returned null. If your effect does not require clean ' +
685
- 'up, return undefined (or nothing).';
686
- } else if (typeof destroy.then === 'function') {
687
- addendum =
688
- '\n\nIt looks like you wrote ' +
689
- hookName +
690
- '(async () => ...) or returned a Promise. ' +
691
- 'Instead, write the async function inside your effect ' +
692
- 'and call it immediately:\n\n' +
693
- hookName +
694
- '(() => {\n' +
695
- ' async function fetchData() {\n' +
696
- ' // You can await here\n' +
697
- ' const response = await MyAPI.getData(someId);\n' +
698
- ' // ...\n' +
699
- ' }\n' +
700
- ' fetchData();\n' +
701
- `}, [someId]); // Or [] if effect doesn't need props or state\n\n` +
702
- 'Learn more about data fetching with Hooks: https://react.dev/link/hooks-data-fetching';
703
- } else {
704
- addendum = ' You returned: ' + destroy;
705
- }
706
- console.error(
707
- '%s must not return anything besides a function, ' +
708
- 'which is used for clean-up.%s',
709
- hookName,
710
- addendum,
711
- );
712
- }
713
- }
714
- }
715
- effect = effect.next;
716
- } while (effect !== firstEffect);
717
- }
718
-}
719
-
720
-function commitUseEffectEventMount(finishedWork: Fiber) {
721
- const updateQueue: FunctionComponentUpdateQueue | null =
722
- (finishedWork.updateQueue: any);
723
- const eventPayloads = updateQueue !== null ? updateQueue.events : null;
724
- if (eventPayloads !== null) {
725
- for (let ii = 0; ii < eventPayloads.length; ii++) {
726
- const {ref, nextImpl} = eventPayloads[ii];
727
- ref.impl = nextImpl;
728
- }
729
- }
730
-}
731
-
400
export function commitPassiveEffectDurations(
401
finishedRoot: FiberRoot,
402
finishedWork: Fiber,
@@ -785,293 +453,6 @@ export function commitPassiveEffectDurations(
453
}
454
}
455
788
-function commitHookLayoutEffects(finishedWork: Fiber, hookFlags: HookFlags) {
789
- // At this point layout effects have already been destroyed (during mutation phase).
790
- // This is done to prevent sibling component effects from interfering with each other,
791
- // e.g. a destroy function in one component should never override a ref set
792
- // by a create function in another component during the same commit.
793
- if (shouldProfile(finishedWork)) {
794
- try {
795
- startLayoutEffectTimer();
796
- commitHookEffectListMount(hookFlags, finishedWork);
797
- } catch (error) {
798
- captureCommitPhaseError(finishedWork, finishedWork.return, error);
799
- }
800
- recordLayoutEffectDuration(finishedWork);
801
- } else {
802
- try {
803
- commitHookEffectListMount(hookFlags, finishedWork);
804
- } catch (error) {
805
- captureCommitPhaseError(finishedWork, finishedWork.return, error);
806
- }
807
- }
808
-}
809
-
810
-function commitClassLayoutLifecycles(
811
- finishedWork: Fiber,
812
- current: Fiber | null,
813
-) {
814
- const instance = finishedWork.stateNode;
815
- if (current === null) {
816
- // We could update instance props and state here,
817
- // but instead we rely on them being set during last render.
818
- // TODO: revisit this when we implement resuming.
819
- if (__DEV__) {
820
- if (
821
- !finishedWork.type.defaultProps &&
822
- !('ref' in finishedWork.memoizedProps) &&
823
- !didWarnAboutReassigningProps
824
- ) {
825
- if (instance.props !== finishedWork.memoizedProps) {
826
- console.error(
827
- 'Expected %s props to match memoized props before ' +
828
- 'componentDidMount. ' +
829
- 'This might either be because of a bug in React, or because ' +
830
- 'a component reassigns its own `this.props`. ' +
831
- 'Please file an issue.',
832
- getComponentNameFromFiber(finishedWork) || 'instance',
833
- );
834
- }
835
- if (instance.state !== finishedWork.memoizedState) {
836
- console.error(
837
- 'Expected %s state to match memoized state before ' +
838
- 'componentDidMount. ' +
839
- 'This might either be because of a bug in React, or because ' +
840
- 'a component reassigns its own `this.state`. ' +
841
- 'Please file an issue.',
842
- getComponentNameFromFiber(finishedWork) || 'instance',
843
- );
844
- }
845
- }
846
- }
847
- if (shouldProfile(finishedWork)) {
848
- startLayoutEffectTimer();
849
- if (__DEV__) {
850
- callComponentDidMountInDEV(finishedWork, instance);
851
- } else {
852
- try {
853
- instance.componentDidMount();
854
- } catch (error) {
855
- captureCommitPhaseError(finishedWork, finishedWork.return, error);
856
- }
857
- }
858
- recordLayoutEffectDuration(finishedWork);
859
- } else {
860
- if (__DEV__) {
861
- callComponentDidMountInDEV(finishedWork, instance);
862
- } else {
863
- try {
864
- instance.componentDidMount();
865
- } catch (error) {
866
- captureCommitPhaseError(finishedWork, finishedWork.return, error);
867
- }
868
- }
869
- }
870
- } else {
871
- const prevProps = resolveClassComponentProps(
872
- finishedWork.type,
873
- current.memoizedProps,
874
- finishedWork.elementType === finishedWork.type,
875
- );
876
- const prevState = current.memoizedState;
877
- // We could update instance props and state here,
878
- // but instead we rely on them being set during last render.
879
- // TODO: revisit this when we implement resuming.
880
- if (__DEV__) {
881
- if (
882
- !finishedWork.type.defaultProps &&
883
- !('ref' in finishedWork.memoizedProps) &&
884
- !didWarnAboutReassigningProps
885
- ) {
886
- if (instance.props !== finishedWork.memoizedProps) {
887
- console.error(
888
- 'Expected %s props to match memoized props before ' +
889
- 'componentDidUpdate. ' +
890
- 'This might either be because of a bug in React, or because ' +
891
- 'a component reassigns its own `this.props`. ' +
892
- 'Please file an issue.',
893
- getComponentNameFromFiber(finishedWork) || 'instance',
894
- );
895
- }
896
- if (instance.state !== finishedWork.memoizedState) {
897
- console.error(
898
- 'Expected %s state to match memoized state before ' +
899
- 'componentDidUpdate. ' +
900
- 'This might either be because of a bug in React, or because ' +
901
- 'a component reassigns its own `this.state`. ' +
902
- 'Please file an issue.',
903
- getComponentNameFromFiber(finishedWork) || 'instance',
904
- );
905
- }
906
- }
907
- }
908
- if (shouldProfile(finishedWork)) {
909
- startLayoutEffectTimer();
910
- if (__DEV__) {
911
- callComponentDidUpdateInDEV(
912
- finishedWork,
913
- instance,
914
- prevProps,
915
- prevState,
916
- instance.__reactInternalSnapshotBeforeUpdate,
917
- );
918
- } else {
919
- try {
920
- instance.componentDidUpdate(
921
- prevProps,
922
- prevState,
923
- instance.__reactInternalSnapshotBeforeUpdate,
924
- );
925
- } catch (error) {
926
- captureCommitPhaseError(finishedWork, finishedWork.return, error);
927
- }
928
- }
929
- recordLayoutEffectDuration(finishedWork);
930
- } else {
931
- if (__DEV__) {
932
- callComponentDidUpdateInDEV(
933
- finishedWork,
934
- instance,
935
- prevProps,
936
- prevState,
937
- instance.__reactInternalSnapshotBeforeUpdate,
938
- );
939
- } else {
940
- try {
941
- instance.componentDidUpdate(
942
- prevProps,
943
- prevState,
944
- instance.__reactInternalSnapshotBeforeUpdate,
945
- );
946
- } catch (error) {
947
- captureCommitPhaseError(finishedWork, finishedWork.return, error);
948
- }
949
- }
950
- }
951
- }
952
-}
953
-
954
-function commitClassCallbacks(finishedWork: Fiber) {
955
- // TODO: I think this is now always non-null by the time it reaches the
956
- // commit phase. Consider removing the type check.
957
- const updateQueue: UpdateQueue<mixed> | null =
958
- (finishedWork.updateQueue: any);
959
- if (updateQueue !== null) {
960
- const instance = finishedWork.stateNode;
961
- if (__DEV__) {
962
- if (
963
- !finishedWork.type.defaultProps &&
964
- !('ref' in finishedWork.memoizedProps) &&
965
- !didWarnAboutReassigningProps
966
- ) {
967
- if (instance.props !== finishedWork.memoizedProps) {
968
- console.error(
969
- 'Expected %s props to match memoized props before ' +
970
- 'processing the update queue. ' +
971
- 'This might either be because of a bug in React, or because ' +
972
- 'a component reassigns its own `this.props`. ' +
973
- 'Please file an issue.',
974
- getComponentNameFromFiber(finishedWork) || 'instance',
975
- );
976
- }
977
- if (instance.state !== finishedWork.memoizedState) {
978
- console.error(
979
- 'Expected %s state to match memoized state before ' +
980
- 'processing the update queue. ' +
981
- 'This might either be because of a bug in React, or because ' +
982
- 'a component reassigns its own `this.state`. ' +
983
- 'Please file an issue.',
984
- getComponentNameFromFiber(finishedWork) || 'instance',
985
- );
986
- }
987
- }
988
- }
989
- // We could update instance props and state here,
990
- // but instead we rely on them being set during last render.
991
- // TODO: revisit this when we implement resuming.
992
- try {
993
- commitCallbacks(updateQueue, instance);
994
- } catch (error) {
995
- captureCommitPhaseError(finishedWork, finishedWork.return, error);
996
- }
997
- }
998
-}
999
-
1000
-function commitHostComponentMount(finishedWork: Fiber) {
1001
- const type = finishedWork.type;
1002
- const props = finishedWork.memoizedProps;
1003
- const instance: Instance = finishedWork.stateNode;
1004
- try {
1005
- commitMount(instance, type, props, finishedWork);
1006
- } catch (error) {
1007
- captureCommitPhaseError(finishedWork, finishedWork.return, error);
1008
- }
1009
-}
1010
-
1011
-function commitProfilerUpdate(finishedWork: Fiber, current: Fiber | null) {
1012
- if (enableProfilerTimer && getExecutionContext() & CommitContext) {
1013
- try {
1014
- const {onCommit, onRender} = finishedWork.memoizedProps;
1015
- const {effectDuration} = finishedWork.stateNode;
1016
-
1017
- const commitTime = getCommitTime();
1018
-
1019
- let phase = current === null ? 'mount' : 'update';
1020
- if (enableProfilerNestedUpdatePhase) {
1021
- if (isCurrentUpdateNested()) {
1022
- phase = 'nested-update';
1023
- }
1024
- }
1025
-
1026
- if (typeof onRender === 'function') {
1027
- onRender(
1028
- finishedWork.memoizedProps.id,
1029
- phase,
1030
- finishedWork.actualDuration,
1031
- finishedWork.treeBaseDuration,
1032
- finishedWork.actualStartTime,
1033
- commitTime,
1034
- );
1035
- }
1036
-
1037
- if (enableProfilerCommitHooks) {
1038
- if (typeof onCommit === 'function') {
1039
- onCommit(
1040
- finishedWork.memoizedProps.id,
1041
- phase,
1042
- effectDuration,
1043
- commitTime,
1044
- );
1045
- }
1046
-
1047
- // Schedule a passive effect for this Profiler to call onPostCommit hooks.
1048
- // This effect should be scheduled even if there is no onPostCommit callback for this Profiler,
1049
- // because the effect is also where times bubble to parent Profilers.
1050
- enqueuePendingPassiveProfilerEffect(finishedWork);
1051
-
1052
- // Propagate layout effect durations to the next nearest Profiler ancestor.
1053
- // Do not reset these values until the next render so DevTools has a chance to read them first.
1054
- let parentFiber = finishedWork.return;
1055
- outer: while (parentFiber !== null) {
1056
- switch (parentFiber.tag) {
1057
- case HostRoot:
1058
- const root = parentFiber.stateNode;
1059
- root.effectDuration += effectDuration;
1060
- break outer;
1061
- case Profiler:
1062
- const parentStateNode = parentFiber.stateNode;
1063
- parentStateNode.effectDuration += effectDuration;
1064
- break outer;
1065
- }
1066
- parentFiber = parentFiber.return;
1067
- }
1068
- }
1069
- } catch (error) {
1070
- captureCommitPhaseError(finishedWork, finishedWork.return, error);
1071
- }
1072
- }
1073
-}
1074
-
456
function commitLayoutEffectOnFiber(
457
finishedRoot: FiberRoot,
458
current: Fiber | null,
@@ -1121,29 +502,7 @@ function commitLayoutEffectOnFiber(
502
committedLanes,
503
);
504
if (flags & Callback) {
1124
- // TODO: I think this is now always non-null by the time it reaches the
1125
- // commit phase. Consider removing the type check.
1126
- const updateQueue: UpdateQueue<mixed> | null =
1127
- (finishedWork.updateQueue: any);
1128
- if (updateQueue !== null) {
1129
- let instance = null;
1130
- if (finishedWork.child !== null) {
1131
- switch (finishedWork.child.tag) {
1132
- case HostSingleton:
1133
- case HostComponent:
1134
- instance = getPublicInstance(finishedWork.child.stateNode);
1135
- break;
1136
- case ClassComponent:
1137
- instance = finishedWork.child.stateNode;
1138
- break;
1139
- }
1140
- }
1141
- try {
1142
- commitCallbacks(updateQueue, instance);
1143
- } catch (error) {
1144
- captureCommitPhaseError(finishedWork, finishedWork.return, error);
1145
- }
1146
- }
505
+ commitRootCallbacks(finishedWork);
506
}
507
break;
508
}
@@ -1175,7 +534,7 @@ function commitLayoutEffectOnFiber(
534
// These effects should only be committed when components are first mounted,
535
// aka when there is no current/alternate.
536
if (current === null && flags & Update) {
1178
- commitHostComponentMount(finishedWork);
537
+ commitHostMount(finishedWork);
538
}
539
540
if (flags & Ref) {
@@ -1192,7 +551,36 @@ function commitLayoutEffectOnFiber(
551
// TODO: Should this fire inside an offscreen tree? Or should it wait to
552
// fire when the tree becomes visible again.
553
if (flags & Update) {
1195
- commitProfilerUpdate(finishedWork, current);
554
+ const {effectDuration} = finishedWork.stateNode;
555
+
556
+ commitProfilerUpdate(
557
+ finishedWork,
558
+ current,
559
+ getCommitTime(),
560
+ effectDuration,
561
+ );
562
+
563
+ // Schedule a passive effect for this Profiler to call onPostCommit hooks.
564
+ // This effect should be scheduled even if there is no onPostCommit callback for this Profiler,
565
+ // because the effect is also where times bubble to parent Profilers.
566
+ enqueuePendingPassiveProfilerEffect(finishedWork);
567
+
568
+ // Propagate layout effect durations to the next nearest Profiler ancestor.
569
+ // Do not reset these values until the next render so DevTools has a chance to read them first.
570
+ let parentFiber = finishedWork.return;
571
+ outer: while (parentFiber !== null) {
572
+ switch (parentFiber.tag) {
573
+ case HostRoot:
574
+ const root = parentFiber.stateNode;
575
+ root.effectDuration += effectDuration;
576
+ break outer;
577
+ case Profiler:
578
+ const parentStateNode = parentFiber.stateNode;
579
+ parentStateNode.effectDuration += effectDuration;
580
+ break outer;
581
+ }
582
+ parentFiber = parentFiber.return;
583
+ }
584
}
585
break;
586
}
@@ -1558,29 +946,11 @@ function hideOrUnhideAllChildren(finishedWork: Fiber, isHidden: boolean) {
946
) {
947
if (hostSubtreeRoot === null) {
948
hostSubtreeRoot = node;
1561
- try {
1562
- const instance = node.stateNode;
1563
- if (isHidden) {
1564
- hideInstance(instance);
1565
- } else {
1566
- unhideInstance(node.stateNode, node.memoizedProps);
1567
- }
1568
- } catch (error) {
1569
- captureCommitPhaseError(finishedWork, finishedWork.return, error);
1570
- }
949
+ commitShowHideHostInstance(node, isHidden);
950
}
951
} else if (node.tag === HostText) {
952
if (hostSubtreeRoot === null) {
1574
- try {
1575
- const instance = node.stateNode;
1576
- if (isHidden) {
1577
- hideTextInstance(instance);
1578
- } else {
1579
- unhideTextInstance(instance, node.memoizedProps);
1580
- }
1581
- } catch (error) {
1582
- captureCommitPhaseError(finishedWork, finishedWork.return, error);
1583
- }
953
+ commitShowHideHostTextInstance(node, isHidden);
954
}
955
} else if (
956
(node.tag === OffscreenComponent ||
@@ -1604,69 +974,19 @@ function hideOrUnhideAllChildren(finishedWork: Fiber, isHidden: boolean) {
974
return;
975
}
976
1607
- if (hostSubtreeRoot === node) {
1608
- hostSubtreeRoot = null;
1609
- }
1610
-
1611
- node = node.return;
1612
- }
1613
-
1614
- if (hostSubtreeRoot === node) {
1615
- hostSubtreeRoot = null;
1616
- }
1617
-
1618
- node.sibling.return = node.return;
1619
- node = node.sibling;
1620
- }
1621
- }
1622
-}
1623
-
1624
-function commitAttachRef(finishedWork: Fiber) {
1625
- const ref = finishedWork.ref;
1626
- if (ref !== null) {
1627
- const instance = finishedWork.stateNode;
1628
- let instanceToUse;
1629
- switch (finishedWork.tag) {
1630
- case HostHoistable:
1631
- case HostSingleton:
1632
- case HostComponent:
1633
- instanceToUse = getPublicInstance(instance);
1634
- break;
1635
- default:
1636
- instanceToUse = instance;
1637
- }
1638
- // Moved outside to ensure DCE works with this flag
1639
- if (enableScopeAPI && finishedWork.tag === ScopeComponent) {
1640
- instanceToUse = instance;
1641
- }
1642
- if (typeof ref === 'function') {
1643
- if (shouldProfile(finishedWork)) {
1644
- try {
1645
- startLayoutEffectTimer();
1646
- finishedWork.refCleanup = ref(instanceToUse);
1647
- } finally {
1648
- recordLayoutEffectDuration(finishedWork);
1649
- }
1650
- } else {
1651
- finishedWork.refCleanup = ref(instanceToUse);
1652
- }
1653
- } else {
1654
- if (__DEV__) {
1655
- // TODO: We should move these warnings to happen during the render
1656
- // phase (markRef).
1657
- if (disableStringRefs && typeof ref === 'string') {
1658
- console.error('String refs are no longer supported.');
1659
- } else if (!ref.hasOwnProperty('current')) {
1660
- console.error(
1661
- 'Unexpected ref object provided for %s. ' +
1662
- 'Use either a ref-setter function or React.createRef().',
1663
- getComponentNameFromFiber(finishedWork),
1664
- );
977
+ if (hostSubtreeRoot === node) {
978
+ hostSubtreeRoot = null;
979
}
980
+
981
+ node = node.return;
982
+ }
983
+
984
+ if (hostSubtreeRoot === node) {
985
+ hostSubtreeRoot = null;
986
}
987
1668
- // $FlowFixMe[incompatible-use] unable to narrow type to the non-function case
1669
- ref.current = instanceToUse;
988
+ node.sibling.return = node.return;
989
+ node = node.sibling;
990
}
991
}
992
}
@@ -1741,222 +1061,6 @@ function detachFiberAfterEffects(fiber: Fiber) {
1061
fiber.updateQueue = null;
1062
}
1063
1744
-function emptyPortalContainer(current: Fiber) {
1745
- if (!supportsPersistence) {
1746
- return;
1747
- }
1748
-
1749
- const portal: {
1750
- containerInfo: Container,
1751
- pendingChildren: ChildSet,
1752
- ...
1753
- } = current.stateNode;
1754
- const {containerInfo} = portal;
1755
- const emptyChildSet = createContainerChildSet();
1756
- replaceContainerChildren(containerInfo, emptyChildSet);
1757
-}
1758
-
1759
-function getHostParentFiber(fiber: Fiber): Fiber {
1760
- let parent = fiber.return;
1761
- while (parent !== null) {
1762
- if (isHostParent(parent)) {
1763
- return parent;
1764
- }
1765
- parent = parent.return;
1766
- }
1767
-
1768
- throw new Error(
1769
- 'Expected to find a host parent. This error is likely caused by a bug ' +
1770
- 'in React. Please file an issue.',
1771
- );
1772
-}
1773
-
1774
-function isHostParent(fiber: Fiber): boolean {
1775
- return (
1776
- fiber.tag === HostComponent ||
1777
- fiber.tag === HostRoot ||
1778
- (supportsResources ? fiber.tag === HostHoistable : false) ||
1779
- (supportsSingletons ? fiber.tag === HostSingleton : false) ||
1780
- fiber.tag === HostPortal
1781
- );
1782
-}
1783
-
1784
-function getHostSibling(fiber: Fiber): ?Instance {
1785
- // We're going to search forward into the tree until we find a sibling host
1786
- // node. Unfortunately, if multiple insertions are done in a row we have to
1787
- // search past them. This leads to exponential search for the next sibling.
1788
- // TODO: Find a more efficient way to do this.
1789
- let node: Fiber = fiber;
1790
- siblings: while (true) {
1791
- // If we didn't find anything, let's try the next sibling.
1792
- while (node.sibling === null) {
1793
- if (node.return === null || isHostParent(node.return)) {
1794
- // If we pop out of the root or hit the parent the fiber we are the
1795
- // last sibling.
1796
- return null;
1797
- }
1798
- // $FlowFixMe[incompatible-type] found when upgrading Flow
1799
- node = node.return;
1800
- }
1801
- node.sibling.return = node.return;
1802
- node = node.sibling;
1803
- while (
1804
- node.tag !== HostComponent &&
1805
- node.tag !== HostText &&
1806
- (!supportsSingletons ? true : node.tag !== HostSingleton) &&
1807
- node.tag !== DehydratedFragment
1808
- ) {
1809
- // If it is not host node and, we might have a host node inside it.
1810
- // Try to search down until we find one.
1811
- if (node.flags & Placement) {
1812
- // If we don't have a child, try the siblings instead.
1813
- continue siblings;
1814
- }
1815
- // If we don't have a child, try the siblings instead.
1816
- // We also skip portals because they are not part of this host tree.
1817
- if (node.child === null || node.tag === HostPortal) {
1818
- continue siblings;
1819
- } else {
1820
- node.child.return = node;
1821
- node = node.child;
1822
- }
1823
- }
1824
- // Check if this host node is stable or about to be placed.
1825
- if (!(node.flags & Placement)) {
1826
- // Found it!
1827
- return node.stateNode;
1828
- }
1829
- }
1830
-}
1831
-
1832
-function commitPlacement(finishedWork: Fiber): void {
1833
- if (!supportsMutation) {
1834
- return;
1835
- }
1836
-
1837
- if (supportsSingletons) {
1838
- if (finishedWork.tag === HostSingleton) {
1839
- // Singletons are already in the Host and don't need to be placed
1840
- // Since they operate somewhat like Portals though their children will
1841
- // have Placement and will get placed inside them
1842
- return;
1843
- }
1844
- }
1845
- // Recursively insert all host nodes into the parent.
1846
- const parentFiber = getHostParentFiber(finishedWork);
1847
-
1848
- switch (parentFiber.tag) {
1849
- case HostSingleton: {
1850
- if (supportsSingletons) {
1851
- const parent: Instance = parentFiber.stateNode;
1852
- const before = getHostSibling(finishedWork);
1853
- // We only have the top Fiber that was inserted but we need to recurse down its
1854
- // children to find all the terminal nodes.
1855
- insertOrAppendPlacementNode(finishedWork, before, parent);
1856
- break;
1857
- }
1858
- // Fall through
1859
- }
1860
- case HostComponent: {
1861
- const parent: Instance = parentFiber.stateNode;
1862
- if (parentFiber.flags & ContentReset) {
1863
- // Reset the text content of the parent before doing any insertions
1864
- resetTextContent(parent);
1865
- // Clear ContentReset from the effect tag
1866
- parentFiber.flags &= ~ContentReset;
1867
- }
1868
-
1869
- const before = getHostSibling(finishedWork);
1870
- // We only have the top Fiber that was inserted but we need to recurse down its
1871
- // children to find all the terminal nodes.
1872
- insertOrAppendPlacementNode(finishedWork, before, parent);
1873
- break;
1874
- }
1875
- case HostRoot:
1876
- case HostPortal: {
1877
- const parent: Container = parentFiber.stateNode.containerInfo;
1878
- const before = getHostSibling(finishedWork);
1879
- insertOrAppendPlacementNodeIntoContainer(finishedWork, before, parent);
1880
- break;
1881
- }
1882
- default:
1883
- throw new Error(
1884
- 'Invalid host parent fiber. This error is likely caused by a bug ' +
1885
- 'in React. Please file an issue.',
1886
- );
1887
- }
1888
-}
1889
-
1890
-function insertOrAppendPlacementNodeIntoContainer(
1891
- node: Fiber,
1892
- before: ?Instance,
1893
- parent: Container,
1894
-): void {
1895
- const {tag} = node;
1896
- const isHost = tag === HostComponent || tag === HostText;
1897
- if (isHost) {
1898
- const stateNode = node.stateNode;
1899
- if (before) {
1900
- insertInContainerBefore(parent, stateNode, before);
1901
- } else {
1902
- appendChildToContainer(parent, stateNode);
1903
- }
1904
- } else if (
1905
- tag === HostPortal ||
1906
- (supportsSingletons ? tag === HostSingleton : false)
1907
- ) {
1908
- // If the insertion itself is a portal, then we don't want to traverse
1909
- // down its children. Instead, we'll get insertions from each child in
1910
- // the portal directly.
1911
- // If the insertion is a HostSingleton then it will be placed independently
1912
- } else {
1913
- const child = node.child;
1914
- if (child !== null) {
1915
- insertOrAppendPlacementNodeIntoContainer(child, before, parent);
1916
- let sibling = child.sibling;
1917
- while (sibling !== null) {
1918
- insertOrAppendPlacementNodeIntoContainer(sibling, before, parent);
1919
- sibling = sibling.sibling;
1920
- }
1921
- }
1922
- }
1923
-}
1924
-
1925
-function insertOrAppendPlacementNode(
1926
- node: Fiber,
1927
- before: ?Instance,
1928
- parent: Instance,
1929
-): void {
1930
- const {tag} = node;
1931
- const isHost = tag === HostComponent || tag === HostText;
1932
- if (isHost) {
1933
- const stateNode = node.stateNode;
1934
- if (before) {
1935
- insertBefore(parent, stateNode, before);
1936
- } else {
1937
- appendChild(parent, stateNode);
1938
- }
1939
- } else if (
1940
- tag === HostPortal ||
1941
- (supportsSingletons ? tag === HostSingleton : false)
1942
- ) {
1943
- // If the insertion itself is a portal, then we don't want to traverse
1944
- // down its children. Instead, we'll get insertions from each child in
1945
- // the portal directly.
1946
- // If the insertion is a HostSingleton then it will be placed independently
1947
- } else {
1948
- const child = node.child;
1949
- if (child !== null) {
1950
- insertOrAppendPlacementNode(child, before, parent);
1951
- let sibling = child.sibling;
1952
- while (sibling !== null) {
1953
- insertOrAppendPlacementNode(sibling, before, parent);
1954
- sibling = sibling.sibling;
1955
- }
1956
- }
1957
- }
1958
-}
1959
-
1064
// These are tracked on the stack as we recursively traverse a
1065
// deleted subtree.
1066
// TODO: Update these during the whole mutation phase, not just during
@@ -2046,6 +1150,7 @@ function commitDeletionEffectsOnFiber(
1150
nearestMountedAncestor: Fiber,
1151
deletedFiber: Fiber,
1152
) {
1153
+ // TODO: Delete this Hook once new DevTools ships everywhere. No longer needed.
1154
onCommitUnmount(deletedFiber);
1155
1156
// The cases in this outer switch modify the stack before they traverse
@@ -2126,12 +1231,16 @@ function commitDeletionEffectsOnFiber(
1231
// Now that all the child effects have unmounted, we can remove the
1232
// node from the tree.
1233
if (hostParentIsContainer) {
2129
- removeChildFromContainer(
1234
+ commitHostRemoveChildFromContainer(
1235
+ deletedFiber,
1236
+ nearestMountedAncestor,
1237
((hostParent: any): Container),
1238
(deletedFiber.stateNode: Instance | TextInstance),
1239
);
1240
} else {
2134
- removeChild(
1241
+ commitHostRemoveChild(
1242
+ deletedFiber,
1243
+ nearestMountedAncestor,
1244
((hostParent: any): Instance),
1245
(deletedFiber.stateNode: Instance | TextInstance),
1246
);
@@ -2150,9 +1259,17 @@ function commitDeletionEffectsOnFiber(
1259
if (enableSuspenseCallback) {
1260
const hydrationCallbacks = finishedRoot.hydrationCallbacks;
1261
if (hydrationCallbacks !== null) {
2153
- const onDeleted = hydrationCallbacks.onDeleted;
2154
- if (onDeleted) {
2155
- onDeleted((deletedFiber.stateNode: SuspenseInstance));
1262
+ try {
1263
+ const onDeleted = hydrationCallbacks.onDeleted;
1264
+ if (onDeleted) {
1265
+ onDeleted((deletedFiber.stateNode: SuspenseInstance));
1266
+ }
1267
+ } catch (error) {
1268
+ captureCommitPhaseError(
1269
+ deletedFiber,
1270
+ nearestMountedAncestor,
1271
+ error,
1272
+ );
1273
}
1274
}
1275
}
@@ -2192,7 +1309,13 @@ function commitDeletionEffectsOnFiber(
1309
hostParent = prevHostParent;
1310
hostParentIsContainer = prevHostParentIsContainer;
1311
} else {
2195
- emptyPortalContainer(deletedFiber);
1312
+ if (supportsPersistence) {
1313
+ commitHostPortalContainerChildren(
1314
+ deletedFiber.stateNode,
1315
+ deletedFiber,
1316
+ createContainerChildSet(),
1317
+ );
1318
+ }
1319
1320
recursivelyTraverseDeletionEffects(
1321
finishedRoot,
@@ -2340,7 +1463,7 @@ function commitDeletionEffectsOnFiber(
1463
}
1464
}
1465
function commitSuspenseCallback(finishedWork: Fiber) {
2343
- // TODO: Move this to passive phase
1466
+ // TODO: Delete this feature. It's not properly covered by DEV features.
1467
const newState: SuspenseState | null = finishedWork.memoizedState;
1468
if (enableSuspenseCallback && newState !== null) {
1469
const suspenseCallback = finishedWork.memoizedProps.suspenseCallback;
@@ -2372,9 +1495,10 @@ function commitSuspenseHydrationCallbacks(
1495
if (prevState !== null) {
1496
const suspenseInstance = prevState.dehydrated;
1497
if (suspenseInstance !== null) {
2375
- try {
2376
- commitHydratedSuspenseInstance(suspenseInstance);
2377
- if (enableSuspenseCallback) {
1498
+ commitHostHydratedSuspense(suspenseInstance, finishedWork);
1499
+ if (enableSuspenseCallback) {
1500
+ try {
1501
+ // TODO: Delete this feature. It's not properly covered by DEV features.
1502
const hydrationCallbacks = finishedRoot.hydrationCallbacks;
1503
if (hydrationCallbacks !== null) {
1504
const onHydrated = hydrationCallbacks.onHydrated;
@@ -2382,9 +1506,9 @@ function commitSuspenseHydrationCallbacks(
1506
onHydrated(suspenseInstance);
1507
}
1508
}
1509
+ } catch (error) {
1510
+ captureCommitPhaseError(finishedWork, finishedWork.return, error);
1511
}
2386
- } catch (error) {
2387
- captureCommitPhaseError(finishedWork, finishedWork.return, error);
1512
}
1513
}
1514
}
@@ -2499,7 +1623,7 @@ function attachSuspenseRetryListeners(
1623
// This function detects when a Suspense boundary goes from visible to hidden.
1624
// It returns false if the boundary is already hidden.
1625
// TODO: Use an effect tag.
2502
-export function isSuspenseBoundaryBeingHidden(
1626
+function isSuspenseBoundaryBeingHidden(
1627
current: Fiber | null,
1628
finishedWork: Fiber,
1629
): boolean {
@@ -2548,11 +1672,7 @@ function recursivelyTraverseMutationEffects(
1672
if (deletions !== null) {
1673
for (let i = 0; i < deletions.length; i++) {
1674
const childToDelete = deletions[i];
2551
- try {
2552
- commitDeletionEffects(root, parentFiber, childToDelete);
2553
- } catch (error) {
2554
- captureCommitPhaseError(childToDelete, parentFiber, error);
2555
- }
1675
+ commitDeletionEffects(root, parentFiber, childToDelete);
1676
}
1677
}
1678
@@ -2600,46 +1720,31 @@ function commitMutationEffectsOnFiber(
1720
commitReconciliationEffects(finishedWork);
1721
1722
if (flags & Update) {
2603
- try {
2604
- commitHookEffectListUnmount(
2605
- HookInsertion | HookHasEffect,
2606
- finishedWork,
2607
- finishedWork.return,
2608
- );
2609
- commitHookEffectListMount(
2610
- HookInsertion | HookHasEffect,
2611
- finishedWork,
2612
- );
2613
- } catch (error) {
2614
- captureCommitPhaseError(finishedWork, finishedWork.return, error);
2615
- }
1723
+ commitHookEffectListUnmount(
1724
+ HookInsertion | HookHasEffect,
1725
+ finishedWork,
1726
+ finishedWork.return,
1727
+ );
1728
+ commitHookEffectListMount(HookInsertion | HookHasEffect, finishedWork);
1729
// Layout effects are destroyed during the mutation phase so that all
1730
// destroy functions for all fibers are called before any create functions.
1731
// This prevents sibling component effects from interfering with each other,
1732
// e.g. a destroy function in one component should never override a ref set
1733
// by a create function in another component during the same commit.
1734
if (shouldProfile(finishedWork)) {
2622
- try {
2623
- startLayoutEffectTimer();
2624
- commitHookEffectListUnmount(
2625
- HookLayout | HookHasEffect,
2626
- finishedWork,
2627
- finishedWork.return,
2628
- );
2629
- } catch (error) {
2630
- captureCommitPhaseError(finishedWork, finishedWork.return, error);
2631
- }
1735
+ startLayoutEffectTimer();
1736
+ commitHookEffectListUnmount(
1737
+ HookLayout | HookHasEffect,
1738
+ finishedWork,
1739
+ finishedWork.return,
1740
+ );
1741
recordLayoutEffectDuration(finishedWork);
1742
} else {
2634
- try {
2635
- commitHookEffectListUnmount(
2636
- HookLayout | HookHasEffect,
2637
- finishedWork,
2638
- finishedWork.return,
2639
- );
2640
- } catch (error) {
2641
- captureCommitPhaseError(finishedWork, finishedWork.return, error);
2642
- }
1743
+ commitHookEffectListUnmount(
1744
+ HookLayout | HookHasEffect,
1745
+ finishedWork,
1746
+ finishedWork.return,
1747
+ );
1748
}
1749
}
1750
return;
@@ -2730,17 +1835,11 @@ function commitMutationEffectsOnFiber(
1835
);
1836
}
1837
} else if (newResource === null && finishedWork.stateNode !== null) {
2733
- try {
2734
- commitUpdate(
2735
- finishedWork.stateNode,
2736
- finishedWork.type,
2737
- current.memoizedProps,
2738
- finishedWork.memoizedProps,
2739
- finishedWork,
2740
- );
2741
- } catch (error) {
2742
- captureCommitPhaseError(finishedWork, finishedWork.return, error);
2743
- }
1838
+ commitHostUpdate(
1839
+ finishedWork,
1840
+ finishedWork.memoizedProps,
1841
+ current.memoizedProps,
1842
+ );
1843
}
1844
}
1845
return;
@@ -2752,16 +1851,7 @@ function commitMutationEffectsOnFiber(
1851
if (flags & Update) {
1852
const previousWork = finishedWork.alternate;
1853
if (previousWork === null) {
2755
- const singleton = finishedWork.stateNode;
2756
- const props = finishedWork.memoizedProps;
2757
- // This was a new mount, we need to clear and set initial properties
2758
- clearSingleton(singleton);
2759
- acquireSingletonInstance(
2760
- finishedWork.type,
2761
- props,
2762
- singleton,
2763
- finishedWork,
2764
- );
1854
+ commitHostSingleton(finishedWork);
1855
}
1856
}
1857
}
@@ -2784,30 +1874,20 @@ function commitMutationEffectsOnFiber(
1874
// rely on mutating the flag during commit. Like by setting a flag
1875
// during the render phase instead.
1876
if (finishedWork.flags & ContentReset) {
2787
- const instance: Instance = finishedWork.stateNode;
2788
- try {
2789
- resetTextContent(instance);
2790
- } catch (error) {
2791
- captureCommitPhaseError(finishedWork, finishedWork.return, error);
2792
- }
1877
+ commitHostResetTextContent(finishedWork);
1878
}
1879
1880
if (flags & Update) {
1881
const instance: Instance = finishedWork.stateNode;
1882
if (instance != null) {
1883
// Commit the work prepared earlier.
2799
- const newProps = finishedWork.memoizedProps;
1884
// For hydration we reuse the update path but we treat the oldProps
1885
// as the newProps. The updatePayload will contain the real change in
1886
// this case.
1887
+ const newProps = finishedWork.memoizedProps;
1888
const oldProps =
1889
current !== null ? current.memoizedProps : newProps;
2805
- const type = finishedWork.type;
2806
- try {
2807
- commitUpdate(instance, type, oldProps, newProps, finishedWork);
2808
- } catch (error) {
2809
- captureCommitPhaseError(finishedWork, finishedWork.return, error);
2810
- }
1890
+ commitHostUpdate(finishedWork, newProps, oldProps);
1891
}
1892
}
1893
@@ -2840,7 +1920,6 @@ function commitMutationEffectsOnFiber(
1920
);
1921
}
1922
2843
- const textInstance: TextInstance = finishedWork.stateNode;
1923
const newText: string = finishedWork.memoizedProps;
1924
// For hydration we reuse the update path but we treat the oldProps
1925
// as the newProps. The updatePayload will contain the real change in
@@ -2848,11 +1927,7 @@ function commitMutationEffectsOnFiber(
1927
const oldText: string =
1928
current !== null ? current.memoizedProps : newText;
1929
2851
- try {
2852
- commitTextUpdate(textInstance, oldText, newText);
2853
- } catch (error) {
2854
- captureCommitPhaseError(finishedWork, finishedWork.return, error);
2855
- }
1930
+ commitHostTextUpdate(finishedWork, newText, oldText);
1931
}
1932
}
1933
return;
@@ -2878,26 +1953,12 @@ function commitMutationEffectsOnFiber(
1953
if (current !== null) {
1954
const prevRootState: RootState = current.memoizedState;
1955
if (prevRootState.isDehydrated) {
2881
- try {
2882
- commitHydratedContainer(root.containerInfo);
2883
- } catch (error) {
2884
- captureCommitPhaseError(
2885
- finishedWork,
2886
- finishedWork.return,
2887
- error,
2888
- );
2889
- }
1956
+ commitHostHydratedContainer(root, finishedWork);
1957
}
1958
}
1959
}
1960
if (supportsPersistence) {
2894
- const containerInfo = root.containerInfo;
2895
- const pendingChildren = root.pendingChildren;
2896
- try {
2897
- replaceContainerChildren(containerInfo, pendingChildren);
2898
- } catch (error) {
2899
- captureCommitPhaseError(finishedWork, finishedWork.return, error);
2900
- }
1961
+ commitHostRootContainerChildren(root, finishedWork);
1962
}
1963
}
1964
@@ -2933,14 +1994,11 @@ function commitMutationEffectsOnFiber(
1994
1995
if (flags & Update) {
1996
if (supportsPersistence) {
2936
- const portal = finishedWork.stateNode;
2937
- const containerInfo = portal.containerInfo;
2938
- const pendingChildren = portal.pendingChildren;
2939
- try {
2940
- replaceContainerChildren(containerInfo, pendingChildren);
2941
- } catch (error) {
2942
- captureCommitPhaseError(finishedWork, finishedWork.return, error);
2943
- }
1997
+ commitHostPortalContainerChildren(
1998
+ finishedWork.stateNode,
1999
+ finishedWork,
2000
+ finishedWork.stateNode.pendingChildren,
2001
+ );
2002
}
2003
}
2004
return;
@@ -3138,11 +2196,7 @@ function commitReconciliationEffects(finishedWork: Fiber) {
2196
// before the effects on this fiber have fired.
2197
const flags = finishedWork.flags;
2198
if (flags & Placement) {
3141
- try {
3142
- commitPlacement(finishedWork);
3143
- } catch (error) {
3144
- captureCommitPhaseError(finishedWork, finishedWork.return, error);
3145
- }
2199
+ commitHostPlacement(finishedWork);
2200
// Clear the "placement" from effect tag so that we know that this is
2201
// inserted, before any life-cycles like componentDidMount gets called.
2202
// TODO: findDOMNode doesn't rely on this any more but isMounted does
@@ -3338,23 +2392,9 @@ export function reappearLayoutEffects(
2392
includeWorkInProgressEffects,
2393
);
2394
3341
- // TODO: Check for LayoutStatic flag
3342
- const instance = finishedWork.stateNode;
3343
- if (typeof instance.componentDidMount === 'function') {
3344
- try {
3345
- instance.componentDidMount();
3346
- } catch (error) {
3347
- captureCommitPhaseError(finishedWork, finishedWork.return, error);
3348
- }
3349
- }
2395
+ commitClassDidMount(finishedWork);
2396
3351
- // Commit any callbacks that would have fired while the component
3352
- // was hidden.
3353
- const updateQueue: UpdateQueue<mixed> | null =
3354
- (finishedWork.updateQueue: any);
3355
- if (updateQueue !== null) {
3356
- commitHiddenCallbacks(updateQueue, instance);
3357
- }
2397
+ commitClassHiddenCallbacks(finishedWork);
2398
2399
// If this is newly finished work, check for setState callbacks
2400
if (includeWorkInProgressEffects && flags & Callback) {
@@ -3385,7 +2425,7 @@ export function reappearLayoutEffects(
2425
// These effects should only be committed when components are first mounted,
2426
// aka when there is no current/alternate.
2427
if (includeWorkInProgressEffects && current === null && flags & Update) {
3388
- commitHostComponentMount(finishedWork);
2428
+ commitHostMount(finishedWork);
2429
}
2430
2431
// TODO: Check flags & Ref
@@ -3400,7 +2440,36 @@ export function reappearLayoutEffects(
2440
);
2441
// TODO: Figure out how Profiler updates should work with Offscreen
2442
if (includeWorkInProgressEffects && flags & Update) {
3403
- commitProfilerUpdate(finishedWork, current);
2443
+ const {effectDuration} = finishedWork.stateNode;
2444
+
2445
+ commitProfilerUpdate(
2446
+ finishedWork,
2447
+ current,
2448
+ getCommitTime(),
2449
+ effectDuration,
2450
+ );
2451
+
2452
+ // Schedule a passive effect for this Profiler to call onPostCommit hooks.
2453
+ // This effect should be scheduled even if there is no onPostCommit callback for this Profiler,
2454
+ // because the effect is also where times bubble to parent Profilers.
2455
+ enqueuePendingPassiveProfilerEffect(finishedWork);
2456
+
2457
+ // Propagate layout effect durations to the next nearest Profiler ancestor.
2458
+ // Do not reset these values until the next render so DevTools has a chance to read them first.
2459
+ let parentFiber = finishedWork.return;
2460
+ outer: while (parentFiber !== null) {
2461
+ switch (parentFiber.tag) {
2462
+ case HostRoot:
2463
+ const root = parentFiber.stateNode;
2464
+ root.effectDuration += effectDuration;
2465
+ break outer;
2466
+ case Profiler:
2467
+ const parentStateNode = parentFiber.stateNode;
2468
+ parentStateNode.effectDuration += effectDuration;
2469
+ break outer;
2470
+ }
2471
+ parentFiber = parentFiber.return;
2472
+ }
2473
}
2474
break;
2475
}
@@ -3411,9 +2480,8 @@ export function reappearLayoutEffects(
2480
includeWorkInProgressEffects,
2481
);
2482
3414
- // TODO: Figure out how Suspense hydration callbacks should work
3415
- // with Offscreen.
2483
if (includeWorkInProgressEffects && flags & Update) {
2484
+ // TODO: Delete this feature.
2485
commitSuspenseHydrationCallbacks(finishedRoot, finishedWork);
2486
}
2487
break;
@@ -3482,27 +2550,6 @@ function recursivelyTraverseReappearLayoutEffects(
2550
}
2551
}
2552
3485
-function commitHookPassiveMountEffects(
3486
- finishedWork: Fiber,
3487
- hookFlags: HookFlags,
3488
-) {
3489
- if (shouldProfile(finishedWork)) {
3490
- startPassiveEffectTimer();
3491
- try {
3492
- commitHookEffectListMount(hookFlags, finishedWork);
3493
- } catch (error) {
3494
- captureCommitPhaseError(finishedWork, finishedWork.return, error);
3495
- }
3496
- recordPassiveEffectDuration(finishedWork);
3497
- } else {
3498
- try {
3499
- commitHookEffectListMount(hookFlags, finishedWork);
3500
- } catch (error) {
3501
- captureCommitPhaseError(finishedWork, finishedWork.return, error);
3502
- }
3503
- }
3504
-}
3505
-
2553
function commitOffscreenPassiveMountEffects(
2554
current: Fiber | null,
2555
finishedWork: Fiber,
@@ -4341,28 +3388,6 @@ function detachAlternateSiblings(parentFiber: Fiber) {
3388
}
3389
}
3390
4344
-function commitHookPassiveUnmountEffects(
4345
- finishedWork: Fiber,
4346
- nearestMountedAncestor: null | Fiber,
4347
- hookFlags: HookFlags,
4348
-) {
4349
- if (shouldProfile(finishedWork)) {
4350
- startPassiveEffectTimer();
4351
- commitHookEffectListUnmount(
4352
- hookFlags,
4353
- finishedWork,
4354
- nearestMountedAncestor,
4355
- );
4356
- recordPassiveEffectDuration(finishedWork);
4357
- } else {
4358
- commitHookEffectListUnmount(
4359
- hookFlags,
4360
- finishedWork,
4361
- nearestMountedAncestor,
4362
- );
4363
- }
4364
-}
4365
-
3391
function recursivelyTraversePassiveUnmountEffects(parentFiber: Fiber): void {
3392
// Deletions effects can be scheduled on any fiber type. They need to happen
3393
// before the children effects have fired.
@@ -4696,7 +3721,7 @@ function commitPassiveUnmountInsideDeletedTreeOnFiber(
3721
}
3722
}
3723
4699
-function invokeLayoutEffectMountInDEV(fiber: Fiber): void {
3724
+export function invokeLayoutEffectMountInDEV(fiber: Fiber): void {
3725
if (__DEV__) {
3726
// We don't need to re-check StrictEffectsMode here.
3727
// This function is only called if that check has already passed.
@@ -4704,29 +3729,18 @@ function invokeLayoutEffectMountInDEV(fiber: Fiber): void {
3729
case FunctionComponent:
3730
case ForwardRef:
3731
case SimpleMemoComponent: {
4707
- try {
4708
- commitHookEffectListMount(HookLayout | HookHasEffect, fiber);
4709
- } catch (error) {
4710
- captureCommitPhaseError(fiber, fiber.return, error);
4711
- }
3732
+ commitHookEffectListMount(HookLayout | HookHasEffect, fiber);
3733
break;
3734
}
3735
case ClassComponent: {
4715
- const instance = fiber.stateNode;
4716
- if (typeof instance.componentDidMount === 'function') {
4717
- try {
4718
- instance.componentDidMount();
4719
- } catch (error) {
4720
- captureCommitPhaseError(fiber, fiber.return, error);
4721
- }
4722
- }
3736
+ commitClassDidMount(fiber);
3737
break;
3738
}
3739
}
3740
}
3741
}
3742
4729
-function invokePassiveEffectMountInDEV(fiber: Fiber): void {
3743
+export function invokePassiveEffectMountInDEV(fiber: Fiber): void {
3744
if (__DEV__) {
3745
// We don't need to re-check StrictEffectsMode here.
3746
// This function is only called if that check has already passed.
@@ -4734,18 +3748,14 @@ function invokePassiveEffectMountInDEV(fiber: Fiber): void {
3748
case FunctionComponent:
3749
case ForwardRef:
3750
case SimpleMemoComponent: {
4737
- try {
4738
- commitHookEffectListMount(HookPassive | HookHasEffect, fiber);
4739
- } catch (error) {
4740
- captureCommitPhaseError(fiber, fiber.return, error);
4741
- }
3751
+ commitHookEffectListMount(HookPassive | HookHasEffect, fiber);
3752
break;
3753
}
3754
}
3755
}
3756
}
3757
4748
-function invokeLayoutEffectUnmountInDEV(fiber: Fiber): void {
3758
+export function invokeLayoutEffectUnmountInDEV(fiber: Fiber): void {
3759
if (__DEV__) {
3760
// We don't need to re-check StrictEffectsMode here.
3761
// This function is only called if that check has already passed.
@@ -4753,15 +3763,11 @@ function invokeLayoutEffectUnmountInDEV(fiber: Fiber): void {
3763
case FunctionComponent:
3764
case ForwardRef:
3765
case SimpleMemoComponent: {
4756
- try {
4757
- commitHookEffectListUnmount(
4758
- HookLayout | HookHasEffect,
4759
- fiber,
4760
- fiber.return,
4761
- );
4762
- } catch (error) {
4763
- captureCommitPhaseError(fiber, fiber.return, error);
4764
- }
3766
+ commitHookEffectListUnmount(
3767
+ HookLayout | HookHasEffect,
3768
+ fiber,
3769
+ fiber.return,
3770
+ );
3771
break;
3772
}
3773
case ClassComponent: {
@@ -4775,7 +3781,7 @@ function invokeLayoutEffectUnmountInDEV(fiber: Fiber): void {
3781
}
3782
}
3783
4778
-function invokePassiveEffectUnmountInDEV(fiber: Fiber): void {
3784
+export function invokePassiveEffectUnmountInDEV(fiber: Fiber): void {
3785
if (__DEV__) {
3786
// We don't need to re-check StrictEffectsMode here.
3787
// This function is only called if that check has already passed.
@@ -4783,25 +3789,12 @@ function invokePassiveEffectUnmountInDEV(fiber: Fiber): void {
3789
case FunctionComponent:
3790
case ForwardRef:
3791
case SimpleMemoComponent: {
4786
- try {
4787
- commitHookEffectListUnmount(
4788
- HookPassive | HookHasEffect,
4789
- fiber,
4790
- fiber.return,
4791
- );
4792
- } catch (error) {
4793
- captureCommitPhaseError(fiber, fiber.return, error);
4794
- }
3792
+ commitHookEffectListUnmount(
3793
+ HookPassive | HookHasEffect,
3794
+ fiber,
3795
+ fiber.return,
3796
+ );
3797
}
3798
}
3799
}
3800
}
4799
-
4800
-export {
4801
- commitPlacement,
4802
- commitAttachRef,
4803
- invokeLayoutEffectMountInDEV,
4804
- invokeLayoutEffectUnmountInDEV,
4805
- invokePassiveEffectMountInDEV,
4806
- invokePassiveEffectUnmountInDEV,
4807
-};