Repository: facebook/react
Pull request: [Fizz] Pass batch as argument to revealCompletedBoundaries

Follow up to #33293.

This solves a race condition when boundaries are added to the batch after the `startViewTransition` call.

This doesn't matter yet but it will once we start assigning names before the `startViewTransition` call.

A possible alternative solution might be to ensure the names are added synchronously in the event that adds to the batch. It's possible to keep adding to a batch until the snapshot has happened.

Changed files:
filename: packages/react-dom-bindings/src/server/fizz-instruction-set/ReactDOMFizzInstructionSetInlineCodeStrings.js
patch: @@ -6,9 +6,9 @@ export const markShellTime =
 export const clientRenderBoundary =
   '$RX=function(b,c,d,e,f){var a=document.getElementById(b);a&&(b=a.previousSibling,b.data="$!",a=a.dataset,c&&(a.dgst=c),d&&(a.msg=d),e&&(a.stck=e),f&&(a.cstck=f),b._reactRetry&&b._reactRetry())};';
 export const completeBoundary =
-  '$RB=[];$RV=function(){$RT=performance.now();var d=$RB;$RB=[];for(var a=0;a<d.length;a+=2){var b=d[a],h=d[a+1],e=b.parentNode;if(e){var f=b.previousSibling,g=0;do{if(b&&8===b.nodeType){var c=b.data;if("/$"===c||"/&"===c)if(0===g)break;else g--;else"$"!==c&&"$?"!==c&&"$~"!==c&&"$!"!==c&&"&"!==c||g++}c=b.nextSibling;e.removeChild(b);b=c}while(b);for(;h.firstChild;)e.insertBefore(h.firstChild,b);f.data="$";f._reactRetry&&f._reactRetry()}}};$RC=function(d,a){if(a=document.getElementById(a))if(a.parentNode.removeChild(a),d=document.getElementById(d))d.previousSibling.data="$~",$RB.push(d,a),2===$RB.length&&setTimeout($RV,("number"!==typeof $RT?0:$RT)+300-performance.now())};';
+  '$RB=[];$RV=function(c){$RT=performance.now();for(var a=0;a<c.length;a+=2){var b=c[a],h=c[a+1],e=b.parentNode;if(e){var f=b.previousSibling,g=0;do{if(b&&8===b.nodeType){var d=b.data;if("/$"===d||"/&"===d)if(0===g)break;else g--;else"$"!==d&&"$?"!==d&&"$~"!==d&&"$!"!==d&&"&"!==d||g++}d=b.nextSibling;e.removeChild(b);b=d}while(b);for(;h.firstChild;)e.insertBefore(h.firstChild,b);f.data="$";f._reactRetry&&f._reactRetry()}}c.length=0};$RC=function(c,a){if(a=document.getElementById(a))if(a.parentNode.removeChild(a),c=document.getElementById(c))c.previousSibling.data="$~",$RB.push(c,a),2===$RB.length&&setTimeout($RV.bind(null,$RB),("number"!==typeof $RT?0:$RT)+300-performance.now())};';
 export const completeBoundaryUpgradeToViewTransitions =
-  '$RV=function(a){try{var b=document.__reactViewTransition;if(b){b.finished.then($RV,$RV);return}if(window._useVT){var c=document.__reactViewTransition=document.startViewTransition({update:a,types:[]});c.finished.finally(function(){document.__reactViewTransition===c&&(document.__reactViewTransition=null)});return}}catch(d){}a()}.bind(null,$RV);';
+  '$RV=function(c,a){try{var d=document.__reactViewTransition;if(d){d.finished.finally($RV.bind(null,a));return}if(window._useVT){var b=document.__reactViewTransition=document.startViewTransition({update:c.bind(null,a),types:[]});b.ready.finally(function(){});b.finished.finally(function(){document.__reactViewTransition===b&&(document.__reactViewTransition=null)});$RB=[];return}}catch(e){}c(a)}.bind(null,$RV);';
 export const completeBoundaryWithStyles =
   '$RM=new Map;$RR=function(n,w,p){function u(q){this._p=null;q()}for(var r=new Map,t=document,h,b,e=t.querySelectorAll("link[data-precedence],style[data-precedence]"),v=[],k=0;b=e[k++];)"not all"===b.getAttribute("media")?v.push(b):("LINK"===b.tagName&&$RM.set(b.getAttribute("href"),b),r.set(b.dataset.precedence,h=b));e=0;b=[];var l,a;for(k=!0;;){if(k){var f=p[e++];if(!f){k=!1;e=0;continue}var c=!1,m=0;var d=f[m++];if(a=$RM.get(d)){var g=a._p;c=!0}else{a=t.createElement("link");a.href=d;a.rel=\n"stylesheet";for(a.dataset.precedence=l=f[m++];g=f[m++];)a.setAttribute(g,f[m++]);g=a._p=new Promise(function(q,x){a.onload=u.bind(a,q);a.onerror=u.bind(a,x)});$RM.set(d,a)}d=a.getAttribute("media");!g||d&&!matchMedia(d).matches||b.push(g);if(c)continue}else{a=v[e++];if(!a)break;l=a.getAttribute("data-precedence");a.removeAttribute("media")}c=r.get(l)||h;c===h&&(h=a);r.set(l,a);c?c.parentNode.insertBefore(a,c.nextSibling):(c=t.head,c.insertBefore(a,c.firstChild))}if(p=document.getElementById(n))p.previousSibling.data=\n"$~";Promise.all(b).then($RC.bind(null,n,w),$RX.bind(null,n,"CSS failed to load"))};';
 export const completeSegment =

filename: packages/react-dom-bindings/src/server/fizz-instruction-set/ReactDOMFizzInstructionSetShared.js
patch: @@ -18,10 +18,8 @@ const SUSPENSE_FALLBACK_START_DATA = '$!';
 // working. Closure converts it to a dot access anyway, though, so it's not an
 // urgent issue.
 
-export function revealCompletedBoundaries() {
+export function revealCompletedBoundaries(batch) {
   window['$RT'] = performance.now();
-  const batch = window['$RB'];
-  window['$RB'] = [];
   for (let i = 0; i < batch.length; i += 2) {
     const suspenseIdNode = batch[i];
     const contentNode = batch[i + 1];
@@ -79,37 +77,47 @@ export function revealCompletedBoundaries() {
       suspenseNode['_reactRetry']();
     }
   }
+  batch.length = 0;
 }
 
-export function revealCompletedBoundariesWithViewTransitions(revealBoundaries) {
+export function revealCompletedBoundariesWithViewTransitions(
+  revealBoundaries,
+  batch,
+) {
   try {
     const existingTransition = document['__reactViewTransition'];
     if (existingTransition) {
       // Retry after the previous ViewTransition finishes.
-      existingTransition.finished.then(window['$RV'], window['$RV']);
+      existingTransition.finished.finally(window['$RV'].bind(null, batch));
       return;
     }
     const shouldStartViewTransition = window['_useVT']; // TODO: Detect.
     if (shouldStartViewTransition) {
       const transition = (document['__reactViewTransition'] = document[
         'startViewTransition'
       ]({
-        update: revealBoundaries,
+        update: revealBoundaries.bind(null, batch),
         types: [], // TODO: Add a hard coded type for Suspense reveals.
       }));
+      transition.ready.finally(() => {
+        // TODO
+      });
       transition.finished.finally(() => {
         if (document['__reactViewTransition'] === transition) {
           document['__reactViewTransition'] = null;
         }
       });
+      // Queue any future completions into its own batch since they won't have been
+      // snapshotted by this one.
+      window['$RB'] = [];
       return;
     }
     // Fall through to reveal.
   } catch (x) {
     // Fall through to reveal.
   }
   // ViewTransitions v2 not supported or no ViewTransitions found. Reveal immediately.
-  revealBoundaries();
+  revealBoundaries(batch);
 }
 
 export function clientRenderBoundary(
@@ -182,7 +190,7 @@ export function completeBoundary(suspenseBoundaryID, contentID) {
     // We always schedule the flush in a timer even if it's very low or negative to allow
     // for multiple completeBoundary calls that are already queued to have a chance to
     // make the batch.
-    setTimeout(window['$RV'], msUntilTimeout);
+    setTimeout(window['$RV'].bind(null, window['$RB']), msUntilTimeout);
   }
 }