@@ -1516,7 +1516,7 @@ export function attach(
15161516 currentRoot = rootInstance;
15171517 unmountInstanceRecursively(rootInstance);
15181518 rootToFiberInstanceMap.delete(root);
1519- flushPendingEvents(root );
1519+ flushPendingEvents();
15201520 currentRoot = (null: any);
15211521 });
15221522
@@ -1541,7 +1541,7 @@ export function attach(
15411541 currentRoot = newRoot;
15421542 setRootPseudoKey(currentRoot.id, root.current);
15431543 mountFiberRecursively(root.current, false);
1544- flushPendingEvents(root );
1544+ flushPendingEvents();
15451545 currentRoot = (null: any);
15461546 });
15471547
@@ -2099,7 +2099,7 @@ export function attach(
20992099 }
21002100 }
21012101
2102- function flushPendingEvents(root: Object ): void {
2102+ function flushPendingEvents(): void {
21032103 if (shouldBailoutWithPendingOperations()) {
21042104 // If we aren't profiling, we can just bail out here.
21052105 // No use sending an empty update over the bridge.
@@ -2902,16 +2902,32 @@ export function attach(
29022902 // Let's remove it from the parent SuspenseNode.
29032903 const ioInfo = asyncInfo.awaited;
29042904 const suspendedBySet = parentSuspenseNode.suspendedBy.get(ioInfo);
2905+
29052906 if (
29062907 suspendedBySet === undefined ||
29072908 !suspendedBySet.delete(instance)
29082909 ) {
2909- throw new Error(
2910- 'We are cleaning up async info that was not on the parent Suspense boundary. ' +
2911- 'This is a bug in React.',
2912- );
2910+ // A boundary can await the same IO multiple times.
2911+ // We still want to error if we're trying to remove IO that isn't present on
2912+ // this boundary so we need to check if we've already removed it.
2913+ // We're assuming previousSuspendedBy is a small array so this should be faster
2914+ // than allocating and maintaining a Set.
2915+ let alreadyRemovedIO = false;
2916+ for (let j = 0; j < i; j++) {
2917+ const removedIOInfo = previousSuspendedBy[j].awaited;
2918+ if (removedIOInfo === ioInfo) {
2919+ alreadyRemovedIO = true;
2920+ break;
2921+ }
2922+ }
2923+ if (!alreadyRemovedIO) {
2924+ throw new Error(
2925+ 'We are cleaning up async info that was not on the parent Suspense boundary. ' +
2926+ 'This is a bug in React.',
2927+ );
2928+ }
29132929 }
2914- if (suspendedBySet.size === 0) {
2930+ if (suspendedBySet !== undefined && suspendedBySet .size === 0) {
29152931 parentSuspenseNode.suspendedBy.delete(asyncInfo.awaited);
29162932 }
29172933 if (
@@ -5336,7 +5352,7 @@ export function attach(
53365352
53375353 mountFiberRecursively(root.current, false);
53385354
5339- flushPendingEvents(root );
5355+ flushPendingEvents();
53405356
53415357 needsToFlushComponentLogs = false;
53425358 currentRoot = (null: any);
@@ -5439,6 +5455,9 @@ export function attach(
54395455 unmountInstanceRecursively(rootInstance);
54405456 removeRootPseudoKey(currentRoot.id);
54415457 rootToFiberInstanceMap.delete(root);
5458+ } else if (!prevWasMounted && !nextIsMounted) {
5459+ // We don't need this root anymore.
5460+ rootToFiberInstanceMap.delete(root);
54425461 }
54435462
54445463 if (isProfiling && isProfilingSupported) {
@@ -5462,7 +5481,7 @@ export function attach(
54625481 }
54635482
54645483 // We're done here.
5465- flushPendingEvents(root );
5484+ flushPendingEvents();
54665485
54675486 needsToFlushComponentLogs = false;
54685487
0 commit comments