Skip to content

Commit db71391

Browse files
authored
[Fiber] Instrument the lazy initializer thenable in all cases (facebook#35521)
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
1 parent 4cf9063 commit db71391

File tree

2 files changed

+25
-34
lines changed

2 files changed

+25
-34
lines changed

packages/react-dom/src/__tests__/ReactDOMFizzServer-test.js

Lines changed: 7 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -9551,33 +9551,20 @@ Unfortunately that previous paragraph wasn't quite long enough so I'll continue
95519551

95529552
// Create fresh lazy components for CLIENT
95539553
let resolveClientInner;
9554-
const clientLazyInner = React.lazy(() => {
9554+
const clientLazyInner = React.lazy(async () => {
95559555
Scheduler.log('client lazy inner initializer');
9556-
const payload = {default: <InnerComponent />};
9557-
const promise = new Promise(r => {
9558-
resolveClientInner = () => {
9559-
promise.status = 'fulfilled';
9560-
promise.value = payload;
9561-
r(payload);
9562-
};
9556+
return new Promise(r => {
9557+
resolveClientInner = () => r({default: <InnerComponent />});
95639558
});
9564-
return promise;
95659559
});
95669560

95679561
let resolveClientOuter;
9568-
const clientLazyOuter = React.lazy(() => {
9562+
const clientLazyOuter = React.lazy(async () => {
95699563
Scheduler.log('client lazy outer initializer');
9570-
const payload = {
9571-
default: <OuterComponent innerElement={clientLazyInner} />,
9572-
};
9573-
const promise = new Promise(r => {
9574-
resolveClientOuter = () => {
9575-
promise.status = 'fulfilled';
9576-
promise.value = payload;
9577-
r(payload);
9578-
};
9564+
return new Promise(r => {
9565+
resolveClientOuter = () =>
9566+
r({default: <OuterComponent innerElement={clientLazyInner} />});
95799567
});
9580-
return promise;
95819568
});
95829569

95839570
const hydrationErrors = [];

packages/react/src/ReactLazy.js

Lines changed: 18 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -117,13 +117,15 @@ function lazyInitializer<T>(payload: Payload<T>): T {
117117
// $FlowFixMe
118118
ioInfo.value.value = debugValue;
119119
}
120-
// Make the thenable introspectable
121-
if (thenable.status === undefined) {
122-
const fulfilledThenable: FulfilledThenable<{default: T, ...}> =
123-
(thenable: any);
124-
fulfilledThenable.status = 'fulfilled';
125-
fulfilledThenable.value = moduleObject;
126-
}
120+
}
121+
// Make the thenable introspectable
122+
// TODO we should move the lazy introspection into the resolveLazy
123+
// impl or make suspendedThenable be able to be a lazy itself
124+
if (thenable.status === undefined) {
125+
const fulfilledThenable: FulfilledThenable<{default: T, ...}> =
126+
(thenable: any);
127+
fulfilledThenable.status = 'fulfilled';
128+
fulfilledThenable.value = moduleObject;
127129
}
128130
}
129131
},
@@ -151,13 +153,15 @@ function lazyInitializer<T>(payload: Payload<T>): T {
151153
// $FlowFixMe
152154
ioInfo.value.reason = error;
153155
}
154-
// Make the thenable introspectable
155-
if (thenable.status === undefined) {
156-
const rejectedThenable: RejectedThenable<{default: T, ...}> =
157-
(thenable: any);
158-
rejectedThenable.status = 'rejected';
159-
rejectedThenable.reason = error;
160-
}
156+
}
157+
// Make the thenable introspectable
158+
// TODO we should move the lazy introspection into the resolveLazy
159+
// impl or make suspendedThenable be able to be a lazy itself
160+
if (thenable.status === undefined) {
161+
const rejectedThenable: RejectedThenable<{default: T, ...}> =
162+
(thenable: any);
163+
rejectedThenable.status = 'rejected';
164+
rejectedThenable.reason = error;
161165
}
162166
}
163167
},

0 commit comments

Comments
 (0)