@@ -2890,11 +2890,6 @@ export function attach(
28902890 // is no longer in the new set.
28912891 if (previousSuspendedBy !== null && parentSuspenseNode !== null) {
28922892 const nextSuspendedBy = instance.suspendedBy;
2893- // A boundary can await the same IO multiple times.
2894- // We still want to error if we're trying to remove IO that isn't present on
2895- // this boundary. We're tracking the IO we already removed so that we can error
2896- // every time a delete fails.
2897- const removedIOInfos = new Set<ReactIOInfo>();
28982893 for (let i = 0; i < previousSuspendedBy.length; i++) {
28992894 const asyncInfo = previousSuspendedBy[i];
29002895 if (
@@ -2907,16 +2902,28 @@ export function attach(
29072902 // Let's remove it from the parent SuspenseNode.
29082903 const ioInfo = asyncInfo.awaited;
29092904 const suspendedBySet = parentSuspenseNode.suspendedBy.get(ioInfo);
2905+ // A boundary can await the same IO multiple times.
2906+ // We still want to error if we're trying to remove IO that isn't present on
2907+ // this boundary so we need to check if we've already removed it.
2908+ // We're assuming previousSuspendedBy is a small array so this should be faster
2909+ // than allocating and maintaining a Set.
2910+ let alreadyRemovedIO = false;
2911+ for (let j = 0; j < i; j++) {
2912+ const removedIOInfo = previousSuspendedBy[j].awaited;
2913+ if (removedIOInfo === ioInfo) {
2914+ alreadyRemovedIO = true;
2915+ break;
2916+ }
2917+ }
29102918 if (
29112919 suspendedBySet === undefined ||
2912- (!removedIOInfos.has(ioInfo) && !suspendedBySet.delete(instance))
2920+ (!alreadyRemovedIO && !suspendedBySet.delete(instance))
29132921 ) {
29142922 throw new Error(
29152923 'We are cleaning up async info that was not on the parent Suspense boundary. ' +
29162924 'This is a bug in React.',
29172925 );
29182926 }
2919- removedIOInfos.add(ioInfo);
29202927 if (suspendedBySet.size === 0) {
29212928 parentSuspenseNode.suspendedBy.delete(asyncInfo.awaited);
29222929 }
0 commit comments