Repository: facebook/react
Pull request: [Fiber] Instrument the lazy initializer thenable in all cases

When a lazy element or component is initialized a thenable is returned which was only be conditionally instrumented in dev when asyncDebugInfo was enabled. When instrumented these thenables can be used in conjunction with the SuspendOnImmediate optimization where if a thenable resolves before the stack unwinds we can continue rendering from the last suspended fiber. Without this change a recent fix to the useId implementation cannot be easily tested in production because this optimization pathway isn't available to regular React.lazy thenables. To land the prior PR I changed the thenables to a custom type so I could instrument manually in the test. WIth this change we can just use a regular Promise since ReactLazy will instrument in all environments/flags now

Changed files:
filename: packages/react-dom/src/__tests__/ReactDOMFizzServer-test.js
patch: @@ -9551,33 +9551,20 @@ Unfortunately that previous paragraph wasn't quite long enough so I'll continue
 
     // Create fresh lazy components for CLIENT
     let resolveClientInner;
-    const clientLazyInner = React.lazy(() => {
+    const clientLazyInner = React.lazy(async () => {
       Scheduler.log('client lazy inner initializer');
-      const payload = {default: <InnerComponent />};
-      const promise = new Promise(r => {
-        resolveClientInner = () => {
-          promise.status = 'fulfilled';
-          promise.value = payload;
-          r(payload);
-        };
+      return new Promise(r => {
+        resolveClientInner = () => r({default: <InnerComponent />});
       });
-      return promise;
     });
 
     let resolveClientOuter;
-    const clientLazyOuter = React.lazy(() => {
+    const clientLazyOuter = React.lazy(async () => {
       Scheduler.log('client lazy outer initializer');
-      const payload = {
-        default: <OuterComponent innerElement={clientLazyInner} />,
-      };
-      const promise = new Promise(r => {
-        resolveClientOuter = () => {
-          promise.status = 'fulfilled';
-          promise.value = payload;
-          r(payload);
-        };
+      return new Promise(r => {
+        resolveClientOuter = () =>
+          r({default: <OuterComponent innerElement={clientLazyInner} />});
       });
-      return promise;
     });
 
     const hydrationErrors = [];

filename: packages/react/src/ReactLazy.js
patch: @@ -117,13 +117,15 @@ function lazyInitializer<T>(payload: Payload<T>): T {
               // $FlowFixMe
               ioInfo.value.value = debugValue;
             }
-            // Make the thenable introspectable
-            if (thenable.status === undefined) {
-              const fulfilledThenable: FulfilledThenable<{default: T, ...}> =
-                (thenable: any);
-              fulfilledThenable.status = 'fulfilled';
-              fulfilledThenable.value = moduleObject;
-            }
+          }
+          // Make the thenable introspectable
+          // TODO we should move the lazy introspection into the resolveLazy
+          // impl or make suspendedThenable be able to be a lazy itself
+          if (thenable.status === undefined) {
+            const fulfilledThenable: FulfilledThenable<{default: T, ...}> =
+              (thenable: any);
+            fulfilledThenable.status = 'fulfilled';
+            fulfilledThenable.value = moduleObject;
           }
         }
       },
@@ -151,13 +153,15 @@ function lazyInitializer<T>(payload: Payload<T>): T {
               // $FlowFixMe
               ioInfo.value.reason = error;
             }
-            // Make the thenable introspectable
-            if (thenable.status === undefined) {
-              const rejectedThenable: RejectedThenable<{default: T, ...}> =
-                (thenable: any);
-              rejectedThenable.status = 'rejected';
-              rejectedThenable.reason = error;
-            }
+          }
+          // Make the thenable introspectable
+          // TODO we should move the lazy introspection into the resolveLazy
+          // impl or make suspendedThenable be able to be a lazy itself
+          if (thenable.status === undefined) {
+            const rejectedThenable: RejectedThenable<{default: T, ...}> =
+              (thenable: any);
+            rejectedThenable.status = 'rejected';
+            rejectedThenable.reason = error;
           }
         }
       },