@samitouri / QOS-React-2 / commits / 7a73d6a0f9

(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;