(Temporarily) revert unmounting error boundaries changes (#20147)
This reverts commits bcca5a6ca78b33504e0a328c411b043261e7e303 and ffb749c95e0361b3cfbbfc4e1a73bfa2fda0aa93, although neither revert cleanly since methods have been moved between the work-loop and commit-work files. This commit is a mostly manual effort of undoing the changes.
Brian Vaughn committed
Nov 9, 2020 at 10:14 UTC
7a73d6a0f9ad43a6dce145e3cc47e88cc59fbbd7
17 files changed
+100
-931
packages/react-dom/src/__tests__/ReactErrorBoundaries-test.internal.js
-171
@@ -2473,175 +2473,4 @@ describe('ReactErrorBoundaries', () => {
2473
'Caught an error: gotta catch em all.',
2474
);
2475
});
2476
-
2477
- // @gate skipUnmountedBoundaries
2478
- it('catches errors thrown in componentWillUnmount', () => {
2479
- class LocalErrorBoundary extends React.Component {
2480
- state = {error: null};
2481
- static getDerivedStateFromError(error) {
2482
- Scheduler.unstable_yieldValue(
2483
- `ErrorBoundary static getDerivedStateFromError`,
2484
- );
2485
- return {error};
2486
- }
2487
- render() {
2488
- const {children, id, fallbackID} = this.props;
2489
- const {error} = this.state;
2490
- if (error) {
2491
- Scheduler.unstable_yieldValue(`${id} render error`);
2492
- return <Component id={fallbackID} />;
2493
- }
2494
- Scheduler.unstable_yieldValue(`${id} render success`);
2495
- return children || null;
2496
- }
2497
- }
2498
-
2499
- class Component extends React.Component {
2500
- render() {
2501
- const {id} = this.props;
2502
- Scheduler.unstable_yieldValue('Component render ' + id);
2503
- return id;
2504
- }
2505
- }
2506
-
2507
- class LocalBrokenComponentWillUnmount extends React.Component {
2508
- componentWillUnmount() {
2509
- Scheduler.unstable_yieldValue(
2510
- 'BrokenComponentWillUnmount componentWillUnmount',
2511
- );
2512
- throw Error('Expected');
2513
- }
2514
-
2515
- render() {
2516
- Scheduler.unstable_yieldValue('BrokenComponentWillUnmount render');
2517
- return 'broken';
2518
- }
2519
- }
2520
-
2521
- const container = document.createElement('div');
2522
-
2523
- ReactDOM.render(
2524
- <LocalErrorBoundary id="OuterBoundary" fallbackID="OuterFallback">
2525
- <Component id="sibling" />
2526
- <LocalErrorBoundary id="InnerBoundary" fallbackID="InnerFallback">
2527
- <LocalBrokenComponentWillUnmount />
2528
- </LocalErrorBoundary>
2529
- </LocalErrorBoundary>,
2530
- container,
2531
- );
2532
-
2533
- expect(container.firstChild.textContent).toBe('sibling');
2534
- expect(container.lastChild.textContent).toBe('broken');
2535
- expect(Scheduler).toHaveYielded([
2536
- 'OuterBoundary render success',
2537
- 'Component render sibling',
2538
- 'InnerBoundary render success',
2539
- 'BrokenComponentWillUnmount render',
2540
- ]);
2541
-
2542
- ReactDOM.render(
2543
- <LocalErrorBoundary id="OuterBoundary" fallbackID="OuterFallback">
2544
- <Component id="sibling" />
2545
- </LocalErrorBoundary>,
2546
- container,
2547
- );
2548
-
2549
- // React should skip over the unmounting boundary and find the nearest still-mounted boundary.
2550
- expect(container.firstChild.textContent).toBe('OuterFallback');
2551
- expect(container.lastChild.textContent).toBe('OuterFallback');
2552
- expect(Scheduler).toHaveYielded([
2553
- 'OuterBoundary render success',
2554
- 'Component render sibling',
2555
- 'BrokenComponentWillUnmount componentWillUnmount',
2556
- 'ErrorBoundary static getDerivedStateFromError',
2557
- 'OuterBoundary render error',
2558
- 'Component render OuterFallback',
2559
- ]);
2560
- });
2561
-
2562
- // @gate skipUnmountedBoundaries
2563
- it('catches errors thrown while detaching refs', () => {
2564
- class LocalErrorBoundary extends React.Component {
2565
- state = {error: null};
2566
- static getDerivedStateFromError(error) {
2567
- Scheduler.unstable_yieldValue(
2568
- `ErrorBoundary static getDerivedStateFromError`,
2569
- );
2570
- return {error};
2571
- }
2572
- render() {
2573
- const {children, id, fallbackID} = this.props;
2574
- const {error} = this.state;
2575
- if (error) {
2576
- Scheduler.unstable_yieldValue(`${id} render error`);
2577
- return <Component id={fallbackID} />;
2578
- }
2579
- Scheduler.unstable_yieldValue(`${id} render success`);
2580
- return children || null;
2581
- }
2582
- }
2583
-
2584
- class Component extends React.Component {
2585
- render() {
2586
- const {id} = this.props;
2587
- Scheduler.unstable_yieldValue('Component render ' + id);
2588
- return id;
2589
- }
2590
- }
2591
-
2592
- class LocalBrokenCallbackRef extends React.Component {
2593
- _ref = ref => {
2594
- Scheduler.unstable_yieldValue('LocalBrokenCallbackRef ref ' + !!ref);
2595
- if (ref === null) {
2596
- throw Error('Expected');
2597
- }
2598
- };
2599
-
2600
- render() {
2601
- Scheduler.unstable_yieldValue('LocalBrokenCallbackRef render');
2602
- return <div ref={this._ref}>ref</div>;
2603
- }
2604
- }
2605
-
2606
- const container = document.createElement('div');
2607
-
2608
- ReactDOM.render(
2609
- <LocalErrorBoundary id="OuterBoundary" fallbackID="OuterFallback">
2610
- <Component id="sibling" />
2611
- <LocalErrorBoundary id="InnerBoundary" fallbackID="InnerFallback">
2612
- <LocalBrokenCallbackRef />
2613
- </LocalErrorBoundary>
2614
- </LocalErrorBoundary>,
2615
- container,
2616
- );
2617
-
2618
- expect(container.firstChild.textContent).toBe('sibling');
2619
- expect(container.lastChild.textContent).toBe('ref');
2620
- expect(Scheduler).toHaveYielded([
2621
- 'OuterBoundary render success',
2622
- 'Component render sibling',
2623
- 'InnerBoundary render success',
2624
- 'LocalBrokenCallbackRef render',
2625
- 'LocalBrokenCallbackRef ref true',
2626
- ]);
2627
-
2628
- ReactDOM.render(
2629
- <LocalErrorBoundary id="OuterBoundary" fallbackID="OuterFallback">
2630
- <Component id="sibling" />
2631
- </LocalErrorBoundary>,
2632
- container,
2633
- );
2634
-
2635
- // React should skip over the unmounting boundary and find the nearest still-mounted boundary.
2636
- expect(container.firstChild.textContent).toBe('OuterFallback');
2637
- expect(container.lastChild.textContent).toBe('OuterFallback');
2638
- expect(Scheduler).toHaveYielded([
2639
- 'OuterBoundary render success',
2640
- 'Component render sibling',
2641
- 'LocalBrokenCallbackRef ref false',
2642
- 'ErrorBoundary static getDerivedStateFromError',
2643
- 'OuterBoundary render error',
2644
- 'Component render OuterFallback',
2645
- ]);
2646
- });
2476
});
packages/react-dom/src/events/__tests__/DOMPluginEventSystem-test.internal.js
+16
-40
@@ -1669,28 +1669,16 @@ describe('DOMPluginEventSystem', () => {
1669
1670
function Test() {
1671
React.useEffect(() => {
1672
- const clearClick1 = setClick1(
1673
- buttonRef.current,
1674
- targetListener1,
1675
- );
1676
- const clearClick2 = setClick2(
1677
- buttonRef.current,
1678
- targetListener2,
1679
- );
1680
- const clearClick3 = setClick3(
1681
- buttonRef.current,
1682
- targetListener3,
1683
- );
1684
- const clearClick4 = setClick4(
1685
- buttonRef.current,
1686
- targetListener4,
1687
- );
1672
+ setClick1(buttonRef.current, targetListener1);
1673
+ setClick2(buttonRef.current, targetListener2);
1674
+ setClick3(buttonRef.current, targetListener3);
1675
+ setClick4(buttonRef.current, targetListener4);
1676
1677
return () => {
1690
- clearClick1();
1691
- clearClick2();
1692
- clearClick3();
1693
- clearClick4();
1678
+ setClick1();
1679
+ setClick2();
1680
+ setClick3();
1681
+ setClick4();
1682
};
1683
});
1684
@@ -1715,28 +1703,16 @@ describe('DOMPluginEventSystem', () => {
1703
1704
function Test2() {
1705
React.useEffect(() => {
1718
- const clearClick1 = setClick1(
1719
- buttonRef.current,
1720
- targetListener1,
1721
- );
1722
- const clearClick2 = setClick2(
1723
- buttonRef.current,
1724
- targetListener2,
1725
- );
1726
- const clearClick3 = setClick3(
1727
- buttonRef.current,
1728
- targetListener3,
1729
- );
1730
- const clearClick4 = setClick4(
1731
- buttonRef.current,
1732
- targetListener4,
1733
- );
1706
+ setClick1(buttonRef.current, targetListener1);
1707
+ setClick2(buttonRef.current, targetListener2);
1708
+ setClick3(buttonRef.current, targetListener3);
1709
+ setClick4(buttonRef.current, targetListener4);
1710
1711
return () => {
1736
- clearClick1();
1737
- clearClick2();
1738
- clearClick3();
1739
- clearClick4();
1712
+ setClick1();
1713
+ setClick2();
1714
+ setClick3();
1715
+ setClick4();
1716
};
1717
});
1718
packages/react-reconciler/src/ReactFiberCommitWork.new.js
+75
-196
@@ -185,11 +185,7 @@ const callComponentWillUnmountWithTimer = function(current, instance) {
185
};
186
187
// Capture errors so they don't interrupt unmounting.
188
-function safelyCallComponentWillUnmount(
189
- current: Fiber,
190
- instance: any,
191
- nearestMountedAncestor: Fiber | null,
192
-) {
188
+function safelyCallComponentWillUnmount(current, instance) {
189
if (__DEV__) {
190
invokeGuardedCallback(
191
null,
@@ -200,19 +196,19 @@ function safelyCallComponentWillUnmount(
196
);
197
if (hasCaughtError()) {
198
const unmountError = clearCaughtError();
203
- captureCommitPhaseError(current, nearestMountedAncestor, unmountError);
199
+ captureCommitPhaseError(current, unmountError);
200
}
201
} else {
202
try {
203
callComponentWillUnmountWithTimer(current, instance);
204
} catch (unmountError) {
209
- captureCommitPhaseError(current, nearestMountedAncestor, unmountError);
205
+ captureCommitPhaseError(current, unmountError);
206
}
207
}
208
}
209
210
/** @noinline */
215
-function safelyDetachRef(current: Fiber, nearestMountedAncestor: Fiber) {
211
+function safelyDetachRef(current: Fiber) {
212
const ref = current.ref;
213
if (ref !== null) {
214
if (typeof ref === 'function') {
@@ -231,7 +227,7 @@ function safelyDetachRef(current: Fiber, nearestMountedAncestor: Fiber) {
227
228
if (hasCaughtError()) {
229
const refError = clearCaughtError();
234
- captureCommitPhaseError(current, nearestMountedAncestor, refError);
230
+ captureCommitPhaseError(current, refError);
231
}
232
} else {
233
try {
@@ -250,7 +246,7 @@ function safelyDetachRef(current: Fiber, nearestMountedAncestor: Fiber) {
246
ref(null);
247
}
248
} catch (refError) {
253
- captureCommitPhaseError(current, nearestMountedAncestor, refError);
249
+ captureCommitPhaseError(current, refError);
250
}
251
}
252
} else {
@@ -259,32 +255,24 @@ function safelyDetachRef(current: Fiber, nearestMountedAncestor: Fiber) {
255
}
256
}
257
262
-export function safelyCallDestroy(
263
- current: Fiber,
264
- nearestMountedAncestor: Fiber | null,
265
- destroy: () => void,
266
-) {
258
+export function safelyCallDestroy(current: Fiber, destroy: () => void) {
259
if (__DEV__) {
260
invokeGuardedCallback(null, destroy, null);
261
if (hasCaughtError()) {
262
const error = clearCaughtError();
271
- captureCommitPhaseError(current, nearestMountedAncestor, error);
263
+ captureCommitPhaseError(current, error);
264
}
265
} else {
266
try {
267
destroy();
268
} catch (error) {
277
- captureCommitPhaseError(current, nearestMountedAncestor, error);
269
+ captureCommitPhaseError(current, error);
270
}
271
}
272
}
273
274
/** @noinline */
283
-function commitHookEffectListUnmount(
284
- flags: HookFlags,
285
- finishedWork: Fiber,
286
- nearestMountedAncestor: Fiber | null,
287
-) {
275
+function commitHookEffectListUnmount(flags: HookFlags, finishedWork: Fiber) {
276
const updateQueue: FunctionComponentUpdateQueue | null = (finishedWork.updateQueue: any);
277
const lastEffect = updateQueue !== null ? updateQueue.lastEffect : null;
278
if (lastEffect !== null) {
@@ -296,7 +284,7 @@ function commitHookEffectListUnmount(
284
const destroy = effect.destroy;
285
effect.destroy = undefined;
286
if (destroy !== undefined) {
299
- safelyCallDestroy(finishedWork, nearestMountedAncestor, destroy);
287
+ safelyCallDestroy(finishedWork, destroy);
288
}
289
}
290
effect = effect.next;
@@ -442,14 +430,14 @@ function recursivelyCommitBeforeMutationEffects(firstChild: Fiber) {
430
);
431
if (hasCaughtError()) {
432
const error = clearCaughtError();
445
- captureCommitPhaseError(fiber, fiber.return, error);
433
+ captureCommitPhaseError(fiber, error);
434
}
435
resetCurrentDebugFiberInDEV();
436
} else {
437
try {
438
commitBeforeMutationEffectsOnFiber(fiber);
439
} catch (error) {
452
- captureCommitPhaseError(fiber, fiber.return, error);
440
+ captureCommitPhaseError(fiber, error);
441
}
442
}
443
fiber = fiber.sibling;
@@ -492,14 +480,14 @@ function iterativelyCommitBeforeMutationEffects_complete() {
480
);
481
if (hasCaughtError()) {
482
const error = clearCaughtError();
495
- captureCommitPhaseError(fiber, fiber.return, error);
483
+ captureCommitPhaseError(fiber, error);
484
}
485
resetCurrentDebugFiberInDEV();
486
} else {
487
try {
488
commitBeforeMutationEffectsOnFiber(fiber);
489
} catch (error) {
502
- captureCommitPhaseError(fiber, fiber.return, error);
490
+ captureCommitPhaseError(fiber, error);
491
}
492
}
493
@@ -668,12 +656,7 @@ function recursivelyCommitMutationEffects(
656
while (fiber !== null) {
657
const deletions = fiber.deletions;
658
if (deletions !== null) {
671
- commitMutationEffectsDeletions(
672
- deletions,
673
- fiber,
674
- root,
675
- renderPriorityLevel,
676
- );
659
+ commitMutationEffectsDeletions(deletions, root, renderPriorityLevel);
660
}
661
662
if (fiber.child !== null) {
@@ -699,14 +682,14 @@ function recursivelyCommitMutationEffects(
682
);
683
if (hasCaughtError()) {
684
const error = clearCaughtError();
702
- captureCommitPhaseError(fiber, fiber.return, error);
685
+ captureCommitPhaseError(fiber, error);
686
}
687
resetCurrentDebugFiberInDEV();
688
} else {
689
try {
690
commitMutationEffectsOnFiber(fiber, root, renderPriorityLevel);
691
} catch (error) {
709
- captureCommitPhaseError(fiber, fiber.return, error);
692
+ captureCommitPhaseError(fiber, error);
693
}
694
}
695
fiber = fiber.sibling;
@@ -723,12 +706,7 @@ function iterativelyCommitMutationEffects_begin(
706
// TODO: Should wrap this in flags check, too, as optimization
707
const deletions = fiber.deletions;
708
if (deletions !== null) {
726
- commitMutationEffectsDeletions(
727
- deletions,
728
- fiber,
729
- root,
730
- renderPriorityLevel,
731
- );
709
+ commitMutationEffectsDeletions(deletions, root, renderPriorityLevel);
710
}
711
712
const child = fiber.child;
@@ -759,14 +737,14 @@ function iterativelyCommitMutationEffects_complete(
737
);
738
if (hasCaughtError()) {
739
const error = clearCaughtError();
762
- captureCommitPhaseError(fiber, fiber.return, error);
740
+ captureCommitPhaseError(fiber, error);
741
}
742
resetCurrentDebugFiberInDEV();
743
} else {
744
try {
745
commitMutationEffectsOnFiber(fiber, root, renderPriorityLevel);
746
} catch (error) {
769
- captureCommitPhaseError(fiber, fiber.return, error);
747
+ captureCommitPhaseError(fiber, error);
748
}
749
}
750
@@ -855,7 +833,6 @@ function commitMutationEffectsOnFiber(
833
/** @noinline */
834
function commitMutationEffectsDeletions(
835
deletions: Array<Fiber>,
858
- nearestMountedAncestor: Fiber,
836
root: FiberRoot,
837
renderPriorityLevel,
838
) {
@@ -868,23 +845,17 @@ function commitMutationEffectsDeletions(
845
null,
846
root,
847
childToDelete,
871
- nearestMountedAncestor,
848
renderPriorityLevel,
849
);
850
if (hasCaughtError()) {
851
const error = clearCaughtError();
876
- captureCommitPhaseError(childToDelete, nearestMountedAncestor, error);
852
+ captureCommitPhaseError(childToDelete, error);
853
}
854
} else {
855
try {
880
- commitDeletion(
881
- root,
882
- childToDelete,
883
- nearestMountedAncestor,
884
- renderPriorityLevel,
885
- );
856
+ commitDeletion(root, childToDelete, renderPriorityLevel);
857
} catch (error) {
887
- captureCommitPhaseError(childToDelete, nearestMountedAncestor, error);
858
+ captureCommitPhaseError(childToDelete, error);
859
}
860
}
861
}
@@ -906,14 +877,14 @@ export function commitLayoutEffects(
877
);
878
if (hasCaughtError()) {
879
const error = clearCaughtError();
909
- captureCommitPhaseError(finishedWork, null, error);
880
+ captureCommitPhaseError(finishedWork, error);
881
}
882
resetCurrentDebugFiberInDEV();
883
} else {
884
try {
885
recursivelyCommitLayoutEffects(finishedWork, finishedRoot);
886
} catch (error) {
916
- captureCommitPhaseError(finishedWork, null, error);
887
+ captureCommitPhaseError(finishedWork, error);
888
}
889
}
890
} else {
@@ -951,7 +922,7 @@ function recursivelyCommitLayoutEffects(
922
);
923
if (hasCaughtError()) {
924
const error = clearCaughtError();
954
- captureCommitPhaseError(child, finishedWork, error);
925
+ captureCommitPhaseError(child, error);
926
}
927
if (prevCurrentFiberInDEV !== null) {
928
setCurrentDebugFiberInDEV(prevCurrentFiberInDEV);
@@ -962,7 +933,7 @@ function recursivelyCommitLayoutEffects(
933
try {
934
recursivelyCommitLayoutEffects(child, finishedRoot);
935
} catch (error) {
965
- captureCommitPhaseError(child, finishedWork, error);
936
+ captureCommitPhaseError(child, error);
937
}
938
}
939
}
@@ -984,7 +955,7 @@ function recursivelyCommitLayoutEffects(
955
);
956
if (hasCaughtError()) {
957
const error = clearCaughtError();
987
- captureCommitPhaseError(finishedWork, finishedWork.return, error);
958
+ captureCommitPhaseError(finishedWork, error);
959
}
960
if (prevCurrentFiberInDEV !== null) {
961
setCurrentDebugFiberInDEV(prevCurrentFiberInDEV);
@@ -995,7 +966,7 @@ function recursivelyCommitLayoutEffects(
966
try {
967
commitLayoutEffectsForProfiler(finishedWork, finishedRoot);
968
} catch (error) {
998
- captureCommitPhaseError(finishedWork, finishedWork.return, error);
969
+ captureCommitPhaseError(finishedWork, error);
970
}
971
}
972
}
@@ -1036,7 +1007,7 @@ function recursivelyCommitLayoutEffects(
1007
);
1008
if (hasCaughtError()) {
1009
const error = clearCaughtError();
1039
- captureCommitPhaseError(child, finishedWork, error);
1010
+ captureCommitPhaseError(child, error);
1011
}
1012
if (prevCurrentFiberInDEV !== null) {
1013
setCurrentDebugFiberInDEV(prevCurrentFiberInDEV);
@@ -1047,7 +1018,7 @@ function recursivelyCommitLayoutEffects(
1018
try {
1019
recursivelyCommitLayoutEffects(child, finishedRoot);
1020
} catch (error) {
1050
- captureCommitPhaseError(child, finishedWork, error);
1021
+ captureCommitPhaseError(child, error);
1022
}
1023
}
1024
}
@@ -1175,14 +1146,14 @@ function iterativelyCommitLayoutEffects_begin(
1146
);
1147
if (hasCaughtError()) {
1148
const error = clearCaughtError();
1178
- captureCommitPhaseError(finishedWork, finishedWork.return, error);
1149
+ captureCommitPhaseError(finishedWork, error);
1150
}
1151
resetCurrentDebugFiberInDEV();
1152
} else {
1153
try {
1154
commitLayoutEffectsForProfiler(finishedWork, finishedRoot);
1155
} catch (error) {
1185
- captureCommitPhaseError(finishedWork, finishedWork.return, error);
1156
+ captureCommitPhaseError(finishedWork, error);
1157
}
1158
}
1159
}
@@ -1236,14 +1207,14 @@ function iterativelyCommitLayoutEffects_complete(
1207
);
1208
if (hasCaughtError()) {
1209
const error = clearCaughtError();
1239
- captureCommitPhaseError(fiber, fiber.return, error);
1210
+ captureCommitPhaseError(fiber, error);
1211
}
1212
resetCurrentDebugFiberInDEV();
1213
} else {
1214
try {
1215
commitLayoutEffectsOnFiber(finishedRoot, fiber);
1216
} catch (error) {
1246
- captureCommitPhaseError(fiber, fiber.return, error);
1217
+ captureCommitPhaseError(fiber, error);
1218
}
1219
}
1220
}
@@ -1690,14 +1661,14 @@ function recursivelyCommitPassiveMountEffects(
1661
);
1662
if (hasCaughtError()) {
1663
const error = clearCaughtError();
1693
- captureCommitPhaseError(fiber, fiber.return, error);
1664
+ captureCommitPhaseError(fiber, error);
1665
}
1666
resetCurrentDebugFiberInDEV();
1667
} else {
1668
try {
1669
commitPassiveMountOnFiber(root, fiber);
1670
} catch (error) {
1700
- captureCommitPhaseError(fiber, fiber.return, error);
1671
+ captureCommitPhaseError(fiber, error);
1672
}
1673
}
1674
}
@@ -1755,14 +1726,14 @@ function iterativelyCommitPassiveMountEffects_begin(
1726
);
1727
if (hasCaughtError()) {
1728
const error = clearCaughtError();
1758
- captureCommitPhaseError(fiber, fiber.return, error);
1729
+ captureCommitPhaseError(fiber, error);
1730
}
1731
resetCurrentDebugFiberInDEV();
1732
} else {
1733
try {
1734
commitProfilerPassiveEffect(root, fiber);
1735
} catch (error) {
1765
- captureCommitPhaseError(fiber, fiber.return, error);
1736
+ captureCommitPhaseError(fiber, error);
1737
}
1738
}
1739
}
@@ -1816,14 +1787,14 @@ function iterativelyCommitPassiveMountEffects_complete(
1787
);
1788
if (hasCaughtError()) {
1789
const error = clearCaughtError();
1819
- captureCommitPhaseError(fiber, fiber.return, error);
1790
+ captureCommitPhaseError(fiber, error);
1791
}
1792
resetCurrentDebugFiberInDEV();
1793
} else {
1794
try {
1795
commitPassiveMountOnFiber(root, fiber);
1796
} catch (error) {
1826
- captureCommitPhaseError(fiber, fiber.return, error);
1797
+ captureCommitPhaseError(fiber, error);
1798
}
1799
}
1800
}
@@ -1862,7 +1833,6 @@ function recursivelyCommitPassiveUnmountEffects(firstChild: Fiber): void {
1833
const fiberToDelete = deletions[i];
1834
recursivelyCommitPassiveUnmountEffectsInsideOfDeletedTree(
1835
fiberToDelete,
1865
- fiber,
1836
);
1837
1838
// Now that passive effects have been processed, it's safe to detach lingering pointers.
@@ -1906,7 +1876,6 @@ function iterativelyCommitPassiveUnmountEffects_begin() {
1876
nextEffect = fiberToDelete;
1877
iterativelyCommitPassiveUnmountEffectsInsideOfDeletedTree_begin(
1878
fiberToDelete,
1909
- fiber,
1879
);
1880
1881
// Now that passive effects have been processed, it's safe to detach lingering pointers.
@@ -1946,7 +1915,6 @@ function iterativelyCommitPassiveUnmountEffects_complete() {
1915
1916
function recursivelyCommitPassiveUnmountEffectsInsideOfDeletedTree(
1917
fiberToDelete: Fiber,
1949
- nearestMountedAncestor: Fiber,
1918
): void {
1919
if ((fiberToDelete.subtreeFlags & PassiveStatic) !== NoFlags) {
1920
// If any children have passive effects then traverse the subtree.
@@ -1955,27 +1923,20 @@ function recursivelyCommitPassiveUnmountEffectsInsideOfDeletedTree(
1923
// since that would not cover passive effects in siblings.
1924
let child = fiberToDelete.child;
1925
while (child !== null) {
1958
- recursivelyCommitPassiveUnmountEffectsInsideOfDeletedTree(
1959
- child,
1960
- nearestMountedAncestor,
1961
- );
1926
+ recursivelyCommitPassiveUnmountEffectsInsideOfDeletedTree(child);
1927
child = child.sibling;
1928
}
1929
}
1930
1931
if ((fiberToDelete.flags & PassiveStatic) !== NoFlags) {
1932
setCurrentDebugFiberInDEV(fiberToDelete);
1968
- commitPassiveUnmountInsideDeletedTreeOnFiber(
1969
- fiberToDelete,
1970
- nearestMountedAncestor,
1971
- );
1933
+ commitPassiveUnmountInsideDeletedTreeOnFiber(fiberToDelete);
1934
resetCurrentDebugFiberInDEV();
1935
}
1936
}
1937
1938
function iterativelyCommitPassiveUnmountEffectsInsideOfDeletedTree_begin(
1939
deletedSubtreeRoot: Fiber,
1978
- nearestMountedAncestor: Fiber,
1940
) {
1941
while (nextEffect !== null) {
1942
const fiber = nextEffect;
@@ -1986,7 +1947,6 @@ function iterativelyCommitPassiveUnmountEffectsInsideOfDeletedTree_begin(
1947
} else {
1948
iterativelyCommitPassiveUnmountEffectsInsideOfDeletedTree_complete(
1949
deletedSubtreeRoot,
1989
- nearestMountedAncestor,
1950
);
1951
}
1952
}
@@ -1994,16 +1954,12 @@ function iterativelyCommitPassiveUnmountEffectsInsideOfDeletedTree_begin(
1954
1955
function iterativelyCommitPassiveUnmountEffectsInsideOfDeletedTree_complete(
1956
deletedSubtreeRoot: Fiber,
1997
- nearestMountedAncestor: Fiber,
1957
) {
1958
while (nextEffect !== null) {
1959
const fiber = nextEffect;
1960
if ((fiber.flags & PassiveStatic) !== NoFlags) {
1961
setCurrentDebugFiberInDEV(fiber);
2003
- commitPassiveUnmountInsideDeletedTreeOnFiber(
2004
- fiber,
2005
- nearestMountedAncestor,
2006
- );
1962
+ commitPassiveUnmountInsideDeletedTreeOnFiber(fiber);
1963
resetCurrentDebugFiberInDEV();
1964
}
1965
@@ -2118,7 +2074,6 @@ function commitDetachRef(current: Fiber) {
2074
function commitUnmount(
2075
finishedRoot: FiberRoot,
2076
current: Fiber,
2121
- nearestMountedAncestor: Fiber,
2077
renderPriorityLevel: ReactPriorityLevel,
2078
): void {
2079
onCommitUnmount(current);
@@ -2145,10 +2100,10 @@ function commitUnmount(
2100
current.mode & ProfileMode
2101
) {
2102
startLayoutEffectTimer();
2148
- safelyCallDestroy(current, nearestMountedAncestor, destroy);
2103
+ safelyCallDestroy(current, destroy);
2104
recordLayoutEffectDuration(current);
2105
} else {
2151
- safelyCallDestroy(current, nearestMountedAncestor, destroy);
2106
+ safelyCallDestroy(current, destroy);
2107
}
2108
}
2109
}
@@ -2159,19 +2114,15 @@ function commitUnmount(
2114
return;
2115
}
2116
case ClassComponent: {
2162
- safelyDetachRef(current, nearestMountedAncestor);
2117
+ safelyDetachRef(current);
2118
const instance = current.stateNode;
2119
if (typeof instance.componentWillUnmount === 'function') {
2165
- safelyCallComponentWillUnmount(
2166
- current,
2167
- instance,
2168
- nearestMountedAncestor,
2169
- );
2120
+ safelyCallComponentWillUnmount(current, instance);
2121
}
2122
return;
2123
}
2124
case HostComponent: {
2174
- safelyDetachRef(current, nearestMountedAncestor);
2125
+ safelyDetachRef(current);
2126
return;
2127
}
2128
case HostPortal: {
@@ -2179,12 +2130,7 @@ function commitUnmount(
2130
// We are also not using this parent because
2131
// the portal will get pushed immediately.
2132
if (supportsMutation) {
2182
- unmountHostComponents(
2183
- finishedRoot,
2184
- current,
2185
- nearestMountedAncestor,
2186
- renderPriorityLevel,
2187
- );
2133
+ unmountHostComponents(finishedRoot, current, renderPriorityLevel);
2134
} else if (supportsPersistence) {
2135
emptyPortalContainer(current);
2136
}
@@ -2214,7 +2160,7 @@ function commitUnmount(
2160
}
2161
case ScopeComponent: {
2162
if (enableScopeAPI) {
2217
- safelyDetachRef(current, nearestMountedAncestor);
2163
+ safelyDetachRef(current);
2164
}
2165
return;
2166
}
@@ -2224,7 +2170,6 @@ function commitUnmount(
2170
function commitNestedUnmounts(
2171
finishedRoot: FiberRoot,
2172
root: Fiber,
2227
- nearestMountedAncestor: Fiber,
2173
renderPriorityLevel: ReactPriorityLevel,
2174
): void {
2175
// While we're inside a removed host node we don't want to call
@@ -2234,12 +2179,7 @@ function commitNestedUnmounts(
2179
// we do an inner loop while we're still inside the host node.
2180
let node: Fiber = root;
2181
while (true) {
2237
- commitUnmount(
2238
- finishedRoot,
2239
- node,
2240
- nearestMountedAncestor,
2241
- renderPriorityLevel,
2242
- );
2182
+ commitUnmount(finishedRoot, node, renderPriorityLevel);
2183
// Visit children because they may contain more composite or host nodes.
2184
// Skip portals because commitUnmount() currently visits them recursively.
2185
if (
@@ -2520,10 +2460,9 @@ function insertOrAppendPlacementNode(
2460
}
2461
2462
function unmountHostComponents(
2523
- finishedRoot: FiberRoot,
2524
- current: Fiber,
2525
- nearestMountedAncestor: Fiber,
2526
- renderPriorityLevel: ReactPriorityLevel,
2463
+ finishedRoot,
2464
+ current,
2465
+ renderPriorityLevel,
2466
): void {
2467
// We only have the top Fiber that was deleted but we need to recurse down its
2468
// children to find all the terminal nodes.
@@ -2572,12 +2511,7 @@ function unmountHostComponents(
2511
}
2512
2513
if (node.tag === HostComponent || node.tag === HostText) {
2575
- commitNestedUnmounts(
2576
- finishedRoot,
2577
- node,
2578
- nearestMountedAncestor,
2579
- renderPriorityLevel,
2580
- );
2514
+ commitNestedUnmounts(finishedRoot, node, renderPriorityLevel);
2515
// After all the children have unmounted, it is now safe to remove the
2516
// node from the tree.
2517
if (currentParentIsContainer) {
@@ -2594,12 +2528,7 @@ function unmountHostComponents(
2528
// Don't visit children because we already visited them.
2529
} else if (enableFundamentalAPI && node.tag === FundamentalComponent) {
2530
const fundamentalNode = node.stateNode.instance;
2597
- commitNestedUnmounts(
2598
- finishedRoot,
2599
- node,
2600
- nearestMountedAncestor,
2601
- renderPriorityLevel,
2602
- );
2531
+ commitNestedUnmounts(finishedRoot, node, renderPriorityLevel);
2532
// After all the children have unmounted, it is now safe to remove the
2533
// node from the tree.
2534
if (currentParentIsContainer) {
@@ -2651,12 +2580,7 @@ function unmountHostComponents(
2580
continue;
2581
}
2582
} else {
2654
- commitUnmount(
2655
- finishedRoot,
2656
- node,
2657
- nearestMountedAncestor,
2658
- renderPriorityLevel,
2659
- );
2583
+ commitUnmount(finishedRoot, node, renderPriorityLevel);
2584
// Visit children because we may find more host components below.
2585
if (node.child !== null) {
2586
node.child.return = node;
@@ -2686,26 +2610,15 @@ function unmountHostComponents(
2610
function commitDeletion(
2611
finishedRoot: FiberRoot,
2612
current: Fiber,
2689
- nearestMountedAncestor: Fiber,
2613
renderPriorityLevel: ReactPriorityLevel,
2614
): void {
2615
if (supportsMutation) {
2616
// Recursively delete all host nodes from the parent.
2617
// Detach refs and call componentWillUnmount() on the whole subtree.
2695
- unmountHostComponents(
2696
- finishedRoot,
2697
- current,
2698
- nearestMountedAncestor,
2699
- renderPriorityLevel,
2700
- );
2618
+ unmountHostComponents(finishedRoot, current, renderPriorityLevel);
2619
} else {
2620
// Detach refs and call componentWillUnmount() on the whole subtree.
2703
- commitNestedUnmounts(
2704
- finishedRoot,
2705
- current,
2706
- nearestMountedAncestor,
2707
- renderPriorityLevel,
2708
- );
2621
+ commitNestedUnmounts(finishedRoot, current, renderPriorityLevel);
2622
}
2623
const alternate = current.alternate;
2624
detachFiberMutation(current);
@@ -2736,17 +2649,12 @@ function commitWork(current: Fiber | null, finishedWork: Fiber): void {
2649
commitHookEffectListUnmount(
2650
HookLayout | HookHasEffect,
2651
finishedWork,
2739
- finishedWork.return,
2652
);
2653
} finally {
2654
recordLayoutEffectDuration(finishedWork);
2655
}
2656
} else {
2745
- commitHookEffectListUnmount(
2746
- HookLayout | HookHasEffect,
2747
- finishedWork,
2748
- finishedWork.return,
2749
- );
2657
+ commitHookEffectListUnmount(HookLayout | HookHasEffect, finishedWork);
2658
}
2659
return;
2660
}
@@ -2800,20 +2708,12 @@ function commitWork(current: Fiber | null, finishedWork: Fiber): void {
2708
) {
2709
try {
2710
startLayoutEffectTimer();
2803
- commitHookEffectListUnmount(
2804
- HookLayout | HookHasEffect,
2805
- finishedWork,
2806
- finishedWork.return,
2807
- );
2711
+ commitHookEffectListUnmount(HookLayout | HookHasEffect, finishedWork);
2712
} finally {
2713
recordLayoutEffectDuration(finishedWork);
2714
}
2715
} else {
2812
- commitHookEffectListUnmount(
2813
- HookLayout | HookHasEffect,
2814
- finishedWork,
2815
- finishedWork.return,
2816
- );
2716
+ commitHookEffectListUnmount(HookLayout | HookHasEffect, finishedWork);
2717
}
2718
return;
2719
}
@@ -3049,28 +2949,17 @@ function commitPassiveUnmountOnFiber(finishedWork: Fiber): void {
2949
finishedWork.mode & ProfileMode
2950
) {
2951
startPassiveEffectTimer();
3052
- commitHookEffectListUnmount(
3053
- HookPassive | HookHasEffect,
3054
- finishedWork,
3055
- finishedWork.return,
3056
- );
2952
+ commitHookEffectListUnmount(HookPassive | HookHasEffect, finishedWork);
2953
recordPassiveEffectDuration(finishedWork);
2954
} else {
3059
- commitHookEffectListUnmount(
3060
- HookPassive | HookHasEffect,
3061
- finishedWork,
3062
- finishedWork.return,
3063
- );
2955
+ commitHookEffectListUnmount(HookPassive | HookHasEffect, finishedWork);
2956
}
2957
break;
2958
}
2959
}
2960
}
2961
3070
-function commitPassiveUnmountInsideDeletedTreeOnFiber(
3071
- current: Fiber,
3072
- nearestMountedAncestor: Fiber | null,
3073
-): void {
2962
+function commitPassiveUnmountInsideDeletedTreeOnFiber(current: Fiber): void {
2963
switch (current.tag) {
2964
case FunctionComponent:
2965
case ForwardRef:
@@ -3081,18 +2970,10 @@ function commitPassiveUnmountInsideDeletedTreeOnFiber(
2970
current.mode & ProfileMode
2971
) {
2972
startPassiveEffectTimer();
3084
- commitHookEffectListUnmount(
3085
- HookPassive,
3086
- current,
3087
- nearestMountedAncestor,
3088
- );
2973
+ commitHookEffectListUnmount(HookPassive, current);
2974
recordPassiveEffectDuration(current);
2975
} else {
3091
- commitHookEffectListUnmount(
3092
- HookPassive,
3093
- current,
3094
- nearestMountedAncestor,
3095
- );
2976
+ commitHookEffectListUnmount(HookPassive, current);
2977
}
2978
break;
2979
}
@@ -3147,7 +3028,7 @@ function invokeLayoutEffectMountInDEV(fiber: Fiber): void {
3028
);
3029
if (hasCaughtError()) {
3030
const mountError = clearCaughtError();
3150
- captureCommitPhaseError(fiber, fiber.return, mountError);
3031
+ captureCommitPhaseError(fiber, mountError);
3032
}
3033
break;
3034
}
@@ -3156,7 +3037,7 @@ function invokeLayoutEffectMountInDEV(fiber: Fiber): void {
3037
invokeGuardedCallback(null, instance.componentDidMount, instance);
3038
if (hasCaughtError()) {
3039
const mountError = clearCaughtError();
3159
- captureCommitPhaseError(fiber, fiber.return, mountError);
3040
+ captureCommitPhaseError(fiber, mountError);
3041
}
3042
break;
3043
}
@@ -3181,7 +3062,7 @@ function invokePassiveEffectMountInDEV(fiber: Fiber): void {
3062
);
3063
if (hasCaughtError()) {
3064
const mountError = clearCaughtError();
3184
- captureCommitPhaseError(fiber, fiber.return, mountError);
3065
+ captureCommitPhaseError(fiber, mountError);
3066
}
3067
break;
3068
}
@@ -3203,18 +3084,17 @@ function invokeLayoutEffectUnmountInDEV(fiber: Fiber): void {
3084
null,
3085
HookLayout | HookHasEffect,
3086
fiber,
3206
- fiber.return,
3087
);
3088
if (hasCaughtError()) {
3089
const unmountError = clearCaughtError();
3210
- captureCommitPhaseError(fiber, fiber.return, unmountError);
3090
+ captureCommitPhaseError(fiber, unmountError);
3091
}
3092
break;
3093
}
3094
case ClassComponent: {
3095
const instance = fiber.stateNode;
3096
if (typeof instance.componentWillUnmount === 'function') {
3217
- safelyCallComponentWillUnmount(fiber, instance, fiber.return);
3097
+ safelyCallComponentWillUnmount(fiber, instance);
3098
}
3099
break;
3100
}
@@ -3236,11 +3116,10 @@ function invokePassiveEffectUnmountInDEV(fiber: Fiber): void {
3116
null,
3117
HookPassive | HookHasEffect,
3118
fiber,
3239
- fiber.return,
3119
);
3120
if (hasCaughtError()) {
3121
const unmountError = clearCaughtError();
3243
- captureCommitPhaseError(fiber, fiber.return, unmountError);
3122
+ captureCommitPhaseError(fiber, unmountError);
3123
}
3124
break;
3125
}
packages/react-reconciler/src/ReactFiberWorkLoop.new.js
+2
-13
@@ -27,7 +27,6 @@ import {
27
decoupleUpdatePriorityFromScheduler,
28
enableDebugTracing,
29
enableSchedulingProfiler,
30
- skipUnmountedBoundaries,
30
enableDoubleInvokingEffects,
31
} from 'shared/ReactFeatureFlags';
32
import ReactSharedInternals from 'shared/ReactSharedInternals';
@@ -2201,11 +2200,7 @@ function captureCommitPhaseErrorOnRoot(
2200
}
2201
}
2202
2204
-export function captureCommitPhaseError(
2205
- sourceFiber: Fiber,
2206
- nearestMountedAncestor: Fiber | null,
2207
- error: mixed,
2208
-) {
2203
+export function captureCommitPhaseError(sourceFiber: Fiber, error: mixed) {
2204
if (sourceFiber.tag === HostRoot) {
2205
// Error was thrown at the root. There is no parent, so the root
2206
// itself should capture it.
@@ -2213,13 +2208,7 @@ export function captureCommitPhaseError(
2208
return;
2209
}
2210
2216
- let fiber = null;
2217
- if (skipUnmountedBoundaries) {
2218
- fiber = nearestMountedAncestor;
2219
- } else {
2220
- fiber = sourceFiber.return;
2221
- }
2222
-
2211
+ let fiber = sourceFiber.return;
2212
while (fiber !== null) {
2213
if (fiber.tag === HostRoot) {
2214
captureCommitPhaseErrorOnRoot(fiber, sourceFiber, error);
packages/react-reconciler/src/ReactFiberWorkLoop.old.js
+1
-23
@@ -2298,10 +2298,7 @@ function commitBeforeMutationEffects() {
2298
}
2299
}
2300
2301
-function commitMutationEffects(
2302
- root: FiberRoot,
2303
- renderPriorityLevel: ReactPriorityLevel,
2304
-) {
2301
+function commitMutationEffects(root: FiberRoot, renderPriorityLevel) {
2302
// TODO: Should probably move the bulk of this function to commitWork.
2303
while (nextEffect !== null) {
2304
setCurrentDebugFiberInDEV(nextEffect);
@@ -2760,7 +2757,6 @@ export function captureCommitPhaseError(sourceFiber: Fiber, error: mixed) {
2757
}
2758
2759
let fiber = sourceFiber.return;
2763
-
2760
while (fiber !== null) {
2761
if (fiber.tag === HostRoot) {
2762
captureCommitPhaseErrorOnRoot(fiber, sourceFiber, error);
@@ -2786,24 +2782,6 @@ export function captureCommitPhaseError(sourceFiber: Fiber, error: mixed) {
2782
markRootUpdated(root, SyncLane, eventTime);
2783
ensureRootIsScheduled(root, eventTime);
2784
schedulePendingInteractions(root, SyncLane);
2789
- } else {
2790
- // This component has already been unmounted.
2791
- // We can't schedule any follow up work for the root because the fiber is already unmounted,
2792
- // but we can still call the log-only boundary so the error isn't swallowed.
2793
- //
2794
- // TODO This is only a temporary bandaid for the old reconciler fork.
2795
- // We can delete this special case once the new fork is merged.
2796
- if (
2797
- typeof instance.componentDidCatch === 'function' &&
2798
- !isAlreadyFailedLegacyErrorBoundary(instance)
2799
- ) {
2800
- try {
2801
- instance.componentDidCatch(error, errorInfo);
2802
- } catch (errorToIgnore) {
2803
- // TODO Ignore this error? Rethrow it?
2804
- // This is kind of an edge case.
2805
- }
2806
- }
2785
}
2786
return;
2787
}
packages/react-reconciler/src/__tests__/ReactHooksWithNoopRenderer-test.js
-461
@@ -2353,388 +2353,6 @@ describe('ReactHooksWithNoopRenderer', () => {
2353
expect(ReactNoop.getChildren()).toEqual([]);
2354
});
2355
2356
- describe('errors thrown in passive destroy function within unmounted trees', () => {
2357
- let BrokenUseEffectCleanup;
2358
- let ErrorBoundary;
2359
- let DerivedStateOnlyErrorBoundary;
2360
- let LogOnlyErrorBoundary;
2361
-
2362
- beforeEach(() => {
2363
- BrokenUseEffectCleanup = function() {
2364
- useEffect(() => {
2365
- Scheduler.unstable_yieldValue('BrokenUseEffectCleanup useEffect');
2366
- return () => {
2367
- Scheduler.unstable_yieldValue(
2368
- 'BrokenUseEffectCleanup useEffect destroy',
2369
- );
2370
- throw new Error('Expected error');
2371
- };
2372
- }, []);
2373
-
2374
- return 'inner child';
2375
- };
2376
-
2377
- ErrorBoundary = class extends React.Component {
2378
- state = {error: null};
2379
- static getDerivedStateFromError(error) {
2380
- Scheduler.unstable_yieldValue(
2381
- `ErrorBoundary static getDerivedStateFromError`,
2382
- );
2383
- return {error};
2384
- }
2385
- componentDidCatch(error, info) {
2386
- Scheduler.unstable_yieldValue(`ErrorBoundary componentDidCatch`);
2387
- }
2388
- render() {
2389
- if (this.state.error) {
2390
- Scheduler.unstable_yieldValue('ErrorBoundary render error');
2391
- return <span prop="ErrorBoundary fallback" />;
2392
- }
2393
- Scheduler.unstable_yieldValue('ErrorBoundary render success');
2394
- return this.props.children || null;
2395
- }
2396
- };
2397
-
2398
- DerivedStateOnlyErrorBoundary = class extends React.Component {
2399
- state = {error: null};
2400
- static getDerivedStateFromError(error) {
2401
- Scheduler.unstable_yieldValue(
2402
- `DerivedStateOnlyErrorBoundary static getDerivedStateFromError`,
2403
- );
2404
- return {error};
2405
- }
2406
- render() {
2407
- if (this.state.error) {
2408
- Scheduler.unstable_yieldValue(
2409
- 'DerivedStateOnlyErrorBoundary render error',
2410
- );
2411
- return <span prop="DerivedStateOnlyErrorBoundary fallback" />;
2412
- }
2413
- Scheduler.unstable_yieldValue(
2414
- 'DerivedStateOnlyErrorBoundary render success',
2415
- );
2416
- return this.props.children || null;
2417
- }
2418
- };
2419
-
2420
- LogOnlyErrorBoundary = class extends React.Component {
2421
- componentDidCatch(error, info) {
2422
- Scheduler.unstable_yieldValue(
2423
- `LogOnlyErrorBoundary componentDidCatch`,
2424
- );
2425
- }
2426
- render() {
2427
- Scheduler.unstable_yieldValue(`LogOnlyErrorBoundary render`);
2428
- return this.props.children || null;
2429
- }
2430
- };
2431
- });
2432
-
2433
- // @gate old
2434
- it('should call componentDidCatch() for the nearest unmounted log-only boundary', () => {
2435
- function Conditional({showChildren}) {
2436
- if (showChildren) {
2437
- return (
2438
- <LogOnlyErrorBoundary>
2439
- <BrokenUseEffectCleanup />
2440
- </LogOnlyErrorBoundary>
2441
- );
2442
- } else {
2443
- return null;
2444
- }
2445
- }
2446
-
2447
- act(() => {
2448
- ReactNoop.render(
2449
- <ErrorBoundary>
2450
- <Conditional showChildren={true} />
2451
- </ErrorBoundary>,
2452
- );
2453
- });
2454
-
2455
- expect(Scheduler).toHaveYielded([
2456
- 'ErrorBoundary render success',
2457
- 'LogOnlyErrorBoundary render',
2458
- 'BrokenUseEffectCleanup useEffect',
2459
- ]);
2460
-
2461
- act(() => {
2462
- ReactNoop.render(
2463
- <ErrorBoundary>
2464
- <Conditional showChildren={false} />
2465
- </ErrorBoundary>,
2466
- );
2467
- expect(Scheduler).toFlushAndYieldThrough([
2468
- 'ErrorBoundary render success',
2469
- ]);
2470
- });
2471
-
2472
- expect(Scheduler).toHaveYielded([
2473
- 'BrokenUseEffectCleanup useEffect destroy',
2474
- 'LogOnlyErrorBoundary componentDidCatch',
2475
- ]);
2476
- });
2477
-
2478
- // @gate old
2479
- it('should call componentDidCatch() for the nearest unmounted logging-capable boundary', () => {
2480
- function Conditional({showChildren}) {
2481
- if (showChildren) {
2482
- return (
2483
- <ErrorBoundary>
2484
- <BrokenUseEffectCleanup />
2485
- </ErrorBoundary>
2486
- );
2487
- } else {
2488
- return null;
2489
- }
2490
- }
2491
-
2492
- act(() => {
2493
- ReactNoop.render(
2494
- <ErrorBoundary>
2495
- <Conditional showChildren={true} />
2496
- </ErrorBoundary>,
2497
- );
2498
- });
2499
-
2500
- expect(Scheduler).toHaveYielded([
2501
- 'ErrorBoundary render success',
2502
- 'ErrorBoundary render success',
2503
- 'BrokenUseEffectCleanup useEffect',
2504
- ]);
2505
-
2506
- act(() => {
2507
- ReactNoop.render(
2508
- <ErrorBoundary>
2509
- <Conditional showChildren={false} />
2510
- </ErrorBoundary>,
2511
- );
2512
- expect(Scheduler).toFlushAndYieldThrough([
2513
- 'ErrorBoundary render success',
2514
- ]);
2515
- });
2516
-
2517
- expect(Scheduler).toHaveYielded([
2518
- 'BrokenUseEffectCleanup useEffect destroy',
2519
- 'ErrorBoundary componentDidCatch',
2520
- ]);
2521
- });
2522
-
2523
- // @gate old
2524
- it('should not call getDerivedStateFromError for unmounted error boundaries', () => {
2525
- function Conditional({showChildren}) {
2526
- if (showChildren) {
2527
- return (
2528
- <ErrorBoundary>
2529
- <BrokenUseEffectCleanup />
2530
- </ErrorBoundary>
2531
- );
2532
- } else {
2533
- return null;
2534
- }
2535
- }
2536
-
2537
- act(() => {
2538
- ReactNoop.render(<Conditional showChildren={true} />);
2539
- });
2540
-
2541
- expect(Scheduler).toHaveYielded([
2542
- 'ErrorBoundary render success',
2543
- 'BrokenUseEffectCleanup useEffect',
2544
- ]);
2545
-
2546
- act(() => {
2547
- ReactNoop.render(<Conditional showChildren={false} />);
2548
- });
2549
-
2550
- expect(Scheduler).toHaveYielded([
2551
- 'BrokenUseEffectCleanup useEffect destroy',
2552
- 'ErrorBoundary componentDidCatch',
2553
- ]);
2554
- });
2555
-
2556
- // @gate old
2557
- it('should not throw if there are no unmounted logging-capable boundaries to call', () => {
2558
- function Conditional({showChildren}) {
2559
- if (showChildren) {
2560
- return (
2561
- <DerivedStateOnlyErrorBoundary>
2562
- <BrokenUseEffectCleanup />
2563
- </DerivedStateOnlyErrorBoundary>
2564
- );
2565
- } else {
2566
- return null;
2567
- }
2568
- }
2569
-
2570
- act(() => {
2571
- ReactNoop.render(<Conditional showChildren={true} />);
2572
- });
2573
-
2574
- expect(Scheduler).toHaveYielded([
2575
- 'DerivedStateOnlyErrorBoundary render success',
2576
- 'BrokenUseEffectCleanup useEffect',
2577
- ]);
2578
-
2579
- act(() => {
2580
- ReactNoop.render(<Conditional showChildren={false} />);
2581
- });
2582
-
2583
- expect(Scheduler).toHaveYielded([
2584
- 'BrokenUseEffectCleanup useEffect destroy',
2585
- ]);
2586
- });
2587
-
2588
- // @gate new
2589
- it('should use the nearest still-mounted boundary if there are no unmounted boundaries', () => {
2590
- act(() => {
2591
- ReactNoop.render(
2592
- <LogOnlyErrorBoundary>
2593
- <BrokenUseEffectCleanup />
2594
- </LogOnlyErrorBoundary>,
2595
- );
2596
- });
2597
-
2598
- expect(Scheduler).toHaveYielded([
2599
- 'LogOnlyErrorBoundary render',
2600
- 'BrokenUseEffectCleanup useEffect',
2601
- ]);
2602
-
2603
- act(() => {
2604
- ReactNoop.render(<LogOnlyErrorBoundary />);
2605
- });
2606
-
2607
- expect(Scheduler).toHaveYielded([
2608
- 'LogOnlyErrorBoundary render',
2609
- 'BrokenUseEffectCleanup useEffect destroy',
2610
- 'LogOnlyErrorBoundary componentDidCatch',
2611
- ]);
2612
- });
2613
-
2614
- // @gate new
2615
- it('should skip unmounted boundaries and use the nearest still-mounted boundary', () => {
2616
- function Conditional({showChildren}) {
2617
- if (showChildren) {
2618
- return (
2619
- <ErrorBoundary>
2620
- <BrokenUseEffectCleanup />
2621
- </ErrorBoundary>
2622
- );
2623
- } else {
2624
- return null;
2625
- }
2626
- }
2627
-
2628
- act(() => {
2629
- ReactNoop.render(
2630
- <LogOnlyErrorBoundary>
2631
- <Conditional showChildren={true} />
2632
- </LogOnlyErrorBoundary>,
2633
- );
2634
- });
2635
-
2636
- expect(Scheduler).toHaveYielded([
2637
- 'LogOnlyErrorBoundary render',
2638
- 'ErrorBoundary render success',
2639
- 'BrokenUseEffectCleanup useEffect',
2640
- ]);
2641
-
2642
- act(() => {
2643
- ReactNoop.render(
2644
- <LogOnlyErrorBoundary>
2645
- <Conditional showChildren={false} />
2646
- </LogOnlyErrorBoundary>,
2647
- );
2648
- });
2649
-
2650
- expect(Scheduler).toHaveYielded([
2651
- 'LogOnlyErrorBoundary render',
2652
- 'BrokenUseEffectCleanup useEffect destroy',
2653
- 'LogOnlyErrorBoundary componentDidCatch',
2654
- ]);
2655
- });
2656
-
2657
- // @gate new
2658
- it('should call getDerivedStateFromError in the nearest still-mounted boundary', () => {
2659
- function Conditional({showChildren}) {
2660
- if (showChildren) {
2661
- return <BrokenUseEffectCleanup />;
2662
- } else {
2663
- return null;
2664
- }
2665
- }
2666
-
2667
- act(() => {
2668
- ReactNoop.render(
2669
- <ErrorBoundary>
2670
- <Conditional showChildren={true} />
2671
- </ErrorBoundary>,
2672
- );
2673
- });
2674
-
2675
- expect(Scheduler).toHaveYielded([
2676
- 'ErrorBoundary render success',
2677
- 'BrokenUseEffectCleanup useEffect',
2678
- ]);
2679
-
2680
- act(() => {
2681
- ReactNoop.render(
2682
- <ErrorBoundary>
2683
- <Conditional showChildren={false} />
2684
- </ErrorBoundary>,
2685
- );
2686
- });
2687
-
2688
- expect(Scheduler).toHaveYielded([
2689
- 'ErrorBoundary render success',
2690
- 'BrokenUseEffectCleanup useEffect destroy',
2691
- 'ErrorBoundary static getDerivedStateFromError',
2692
- 'ErrorBoundary render error',
2693
- 'ErrorBoundary componentDidCatch',
2694
- ]);
2695
-
2696
- expect(ReactNoop.getChildren()).toEqual([
2697
- span('ErrorBoundary fallback'),
2698
- ]);
2699
- });
2700
-
2701
- // @gate new
2702
- it('should rethrow error if there are no still-mounted boundaries', () => {
2703
- function Conditional({showChildren}) {
2704
- if (showChildren) {
2705
- return (
2706
- <ErrorBoundary>
2707
- <BrokenUseEffectCleanup />
2708
- </ErrorBoundary>
2709
- );
2710
- } else {
2711
- return null;
2712
- }
2713
- }
2714
-
2715
- act(() => {
2716
- ReactNoop.render(<Conditional showChildren={true} />);
2717
- });
2718
-
2719
- expect(Scheduler).toHaveYielded([
2720
- 'ErrorBoundary render success',
2721
- 'BrokenUseEffectCleanup useEffect',
2722
- ]);
2723
-
2724
- expect(() => {
2725
- act(() => {
2726
- ReactNoop.render(<Conditional showChildren={false} />);
2727
- });
2728
- }).toThrow('Expected error');
2729
-
2730
- expect(Scheduler).toHaveYielded([
2731
- 'BrokenUseEffectCleanup useEffect destroy',
2732
- ]);
2733
-
2734
- expect(ReactNoop.getChildren()).toEqual([]);
2735
- });
2736
- });
2737
-
2356
it('calls passive effect destroy functions for memoized components', () => {
2357
const Wrapper = ({children}) => children;
2358
function Child() {
@@ -2971,85 +2589,6 @@ describe('ReactHooksWithNoopRenderer', () => {
2589
'Mount normal [current: 1]',
2590
]);
2591
});
2974
-
2975
- // @gate skipUnmountedBoundaries
2976
- it('catches errors thrown in useLayoutEffect', () => {
2977
- class ErrorBoundary extends React.Component {
2978
- state = {error: null};
2979
- static getDerivedStateFromError(error) {
2980
- Scheduler.unstable_yieldValue(
2981
- `ErrorBoundary static getDerivedStateFromError`,
2982
- );
2983
- return {error};
2984
- }
2985
- render() {
2986
- const {children, id, fallbackID} = this.props;
2987
- const {error} = this.state;
2988
- if (error) {
2989
- Scheduler.unstable_yieldValue(`${id} render error`);
2990
- return <Component id={fallbackID} />;
2991
- }
2992
- Scheduler.unstable_yieldValue(`${id} render success`);
2993
- return children || null;
2994
- }
2995
- }
2996
-
2997
- function Component({id}) {
2998
- Scheduler.unstable_yieldValue('Component render ' + id);
2999
- return <span prop={id} />;
3000
- }
3001
-
3002
- function BrokenLayoutEffectDestroy() {
3003
- useLayoutEffect(() => {
3004
- return () => {
3005
- Scheduler.unstable_yieldValue(
3006
- 'BrokenLayoutEffectDestroy useLayoutEffect destroy',
3007
- );
3008
- throw Error('Expected');
3009
- };
3010
- }, []);
3011
-
3012
- Scheduler.unstable_yieldValue('BrokenLayoutEffectDestroy render');
3013
- return <span prop="broken" />;
3014
- }
3015
-
3016
- ReactNoop.render(
3017
- <ErrorBoundary id="OuterBoundary" fallbackID="OuterFallback">
3018
- <Component id="sibling" />
3019
- <ErrorBoundary id="InnerBoundary" fallbackID="InnerFallback">
3020
- <BrokenLayoutEffectDestroy />
3021
- </ErrorBoundary>
3022
- </ErrorBoundary>,
3023
- );
3024
-
3025
- expect(Scheduler).toFlushAndYield([
3026
- 'OuterBoundary render success',
3027
- 'Component render sibling',
3028
- 'InnerBoundary render success',
3029
- 'BrokenLayoutEffectDestroy render',
3030
- ]);
3031
- expect(ReactNoop.getChildren()).toEqual([
3032
- span('sibling'),
3033
- span('broken'),
3034
- ]);
3035
-
3036
- ReactNoop.render(
3037
- <ErrorBoundary id="OuterBoundary" fallbackID="OuterFallback">
3038
- <Component id="sibling" />
3039
- </ErrorBoundary>,
3040
- );
3041
-
3042
- // React should skip over the unmounting boundary and find the nearest still-mounted boundary.
3043
- expect(Scheduler).toFlushAndYield([
3044
- 'OuterBoundary render success',
3045
- 'Component render sibling',
3046
- 'BrokenLayoutEffectDestroy useLayoutEffect destroy',
3047
- 'ErrorBoundary static getDerivedStateFromError',
3048
- 'OuterBoundary render error',
3049
- 'Component render OuterFallback',
3050
- ]);
3051
- expect(ReactNoop.getChildren()).toEqual([span('OuterFallback')]);
3052
- });
2592
});
2593
2594
describe('useCallback', () => {
packages/react-reconciler/src/__tests__/ReactIncrementalErrorHandling-test.internal.js
+6
-12
@@ -961,7 +961,6 @@ describe('ReactIncrementalErrorHandling', () => {
961
expect(Scheduler).toFlushAndYield(['Foo']);
962
});
963
964
- // @gate skipUnmountedBoundaries
964
it('should not attempt to recover an unmounting error boundary', () => {
965
class Parent extends React.Component {
966
componentWillUnmount() {
@@ -993,17 +992,12 @@ describe('ReactIncrementalErrorHandling', () => {
992
993
ReactNoop.render(<Parent />);
994
expect(Scheduler).toFlushWithoutYielding();
996
-
997
- // Because the error boundary is also unmounting,
998
- // an error in ThrowsOnUnmount should be rethrown.
999
- expect(() => {
1000
- ReactNoop.render(null);
1001
- expect(Scheduler).toFlushAndYield([
1002
- 'Parent componentWillUnmount',
1003
- 'ThrowsOnUnmount componentWillUnmount',
1004
- ]);
1005
- }).toThrow('unmount error');
1006
-
995
+ ReactNoop.render(null);
996
+ expect(Scheduler).toFlushAndYield([
997
+ // Parent unmounts before the error is thrown.
998
+ 'Parent componentWillUnmount',
999
+ 'ThrowsOnUnmount componentWillUnmount',
1000
+ ]);
1001
ReactNoop.render(<Parent />);
1002
});
1003
packages/shared/ReactFeatureFlags.js
-6
@@ -91,12 +91,6 @@ export const enableComponentStackLocations = true;
91
92
export const enableNewReconciler = false;
93
94
-// Errors that are thrown while unmounting (or after in the case of passive effects)
95
-// should bypass any error boundaries that are also unmounting (or have unmounted)
96
-// and be handled by the nearest still-mounted boundary.
97
-// If there are no still-mounted boundaries, the errors should be rethrown.
98
-export const skipUnmountedBoundaries = false;
99
-
94
// --------------------------
95
// Future APIs to be deprecated
96
// --------------------------
packages/shared/forks/ReactFeatureFlags.native-fb.js
-1
@@ -42,7 +42,6 @@ export const warnAboutSpreadingKeyToJSX = false;
42
export const enableComponentStackLocations = false;
43
export const enableLegacyFBSupport = false;
44
export const enableFilterEmptyStringAttributesDOM = false;
45
-export const skipUnmountedBoundaries = false;
45
46
export const enableNewReconciler = false;
47
export const deferRenderPhaseUpdateToNextBatch = true;
packages/shared/forks/ReactFeatureFlags.native-oss.js
-1
@@ -41,7 +41,6 @@ export const warnAboutSpreadingKeyToJSX = false;
41
export const enableComponentStackLocations = false;
42
export const enableLegacyFBSupport = false;
43
export const enableFilterEmptyStringAttributesDOM = false;
44
-export const skipUnmountedBoundaries = false;
44
45
export const enableNewReconciler = false;
46
export const deferRenderPhaseUpdateToNextBatch = true;
packages/shared/forks/ReactFeatureFlags.test-renderer.js
-1
@@ -41,7 +41,6 @@ export const warnAboutSpreadingKeyToJSX = false;
41
export const enableComponentStackLocations = true;
42
export const enableLegacyFBSupport = false;
43
export const enableFilterEmptyStringAttributesDOM = false;
44
-export const skipUnmountedBoundaries = false;
44
45
export const enableNewReconciler = false;
46
export const deferRenderPhaseUpdateToNextBatch = true;
packages/shared/forks/ReactFeatureFlags.test-renderer.native.js
-1
@@ -41,7 +41,6 @@ export const warnAboutSpreadingKeyToJSX = false;
41
export const enableComponentStackLocations = false;
42
export const enableLegacyFBSupport = false;
43
export const enableFilterEmptyStringAttributesDOM = false;
44
-export const skipUnmountedBoundaries = false;
44
45
export const enableNewReconciler = false;
46
export const deferRenderPhaseUpdateToNextBatch = true;
packages/shared/forks/ReactFeatureFlags.test-renderer.www.js
-1
@@ -41,7 +41,6 @@ export const warnAboutSpreadingKeyToJSX = false;
41
export const enableComponentStackLocations = true;
42
export const enableLegacyFBSupport = false;
43
export const enableFilterEmptyStringAttributesDOM = false;
44
-export const skipUnmountedBoundaries = false;
44
45
export const enableNewReconciler = false;
46
export const deferRenderPhaseUpdateToNextBatch = true;
packages/shared/forks/ReactFeatureFlags.testing.js
-1
@@ -41,7 +41,6 @@ export const warnAboutSpreadingKeyToJSX = false;
41
export const enableComponentStackLocations = true;
42
export const enableLegacyFBSupport = false;
43
export const enableFilterEmptyStringAttributesDOM = false;
44
-export const skipUnmountedBoundaries = false;
44
45
export const enableNewReconciler = false;
46
export const deferRenderPhaseUpdateToNextBatch = true;
packages/shared/forks/ReactFeatureFlags.testing.www.js
-1
@@ -41,7 +41,6 @@ export const warnAboutSpreadingKeyToJSX = false;
41
export const enableComponentStackLocations = true;
42
export const enableLegacyFBSupport = !__EXPERIMENTAL__;
43
export const enableFilterEmptyStringAttributesDOM = false;
44
-export const skipUnmountedBoundaries = __EXPERIMENTAL__;
44
45
export const enableNewReconciler = false;
46
export const deferRenderPhaseUpdateToNextBatch = true;
packages/shared/forks/ReactFeatureFlags.www-dynamic.js
-1
@@ -18,7 +18,6 @@ export const disableInputAttributeSyncing = __VARIANT__;
18
export const enableFilterEmptyStringAttributesDOM = __VARIANT__;
19
export const enableLegacyFBSupport = __VARIANT__;
20
export const decoupleUpdatePriorityFromScheduler = __VARIANT__;
21
-export const skipUnmountedBoundaries = __VARIANT__;
21
22
// Enable this flag to help with concurrent mode debugging.
23
// It logs information to the console about React scheduling, rendering, and commit phases.
packages/shared/forks/ReactFeatureFlags.www.js
-1
@@ -26,7 +26,6 @@ export const {
26
deferRenderPhaseUpdateToNextBatch,
27
decoupleUpdatePriorityFromScheduler,
28
enableDebugTracing,
29
- skipUnmountedBoundaries,
29
enableDoubleInvokingEffects,
30
enableUseRefAccessWarning,
31
} = dynamicFeatureFlags;