Skip to content

Commit 2a18f3e

Browse files
committed
Track stack of use() calls
Then pass this as the default if there is no deeper await. This means that use() behaves similar to await.
1 parent 70b9a13 commit 2a18f3e

File tree

3 files changed

+194
-133
lines changed

3 files changed

+194
-133
lines changed

packages/react-server/src/ReactFlightServer.js

Lines changed: 37 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -692,14 +692,14 @@ function serializeThenable(
692692

693693
switch (thenable.status) {
694694
case 'fulfilled': {
695-
forwardDebugInfoFromThenable(request, newTask, thenable);
695+
forwardDebugInfoFromThenable(request, newTask, thenable, null, null);
696696
// We have the resolved value, we can go ahead and schedule it for serialization.
697697
newTask.model = thenable.value;
698698
pingTask(request, newTask);
699699
return newTask.id;
700700
}
701701
case 'rejected': {
702-
forwardDebugInfoFromThenable(request, newTask, thenable);
702+
forwardDebugInfoFromThenable(request, newTask, thenable, null, null);
703703
const x = thenable.reason;
704704
erroredTask(request, newTask, x);
705705
return newTask.id;
@@ -1042,11 +1042,11 @@ function createLazyWrapperAroundWakeable(
10421042
const thenable: Thenable<mixed> = (wakeable: any);
10431043
switch (thenable.status) {
10441044
case 'fulfilled': {
1045-
forwardDebugInfoFromThenable(request, task, thenable);
1045+
forwardDebugInfoFromThenable(request, task, thenable, null, null);
10461046
return thenable.value;
10471047
}
10481048
case 'rejected':
1049-
forwardDebugInfoFromThenable(request, task, thenable);
1049+
forwardDebugInfoFromThenable(request, task, thenable, null, null);
10501050
break;
10511051
default: {
10521052
if (typeof thenable.status === 'string') {
@@ -1366,6 +1366,7 @@ function renderFunctionComponent<Props>(
13661366
}
13671367
}
13681368
} else {
1369+
componentDebugInfo = (null: any);
13691370
prepareToUseHooksForComponent(prevThenableState, null);
13701371
// The secondArg is always undefined in Server Components since refs error early.
13711372
const secondArg = undefined;
@@ -1398,8 +1399,20 @@ function renderFunctionComponent<Props>(
13981399
// We do this at the end so that we don't keep doing this for each retry.
13991400
const trackedThenables = getTrackedThenablesAfterRendering();
14001401
if (trackedThenables !== null) {
1402+
const stacks: Array<Error> =
1403+
__DEV__ && enableAsyncDebugInfo
1404+
? (trackedThenables: any)._stacks ||
1405+
((trackedThenables: any)._stacks = [])
1406+
: (null: any);
14011407
for (let i = 0; i < trackedThenables.length; i++) {
1402-
forwardDebugInfoFromThenable(request, task, trackedThenables[i]);
1408+
const stack = __DEV__ && enableAsyncDebugInfo ? stacks[i] : null;
1409+
forwardDebugInfoFromThenable(
1410+
request,
1411+
task,
1412+
trackedThenables[i],
1413+
__DEV__ ? componentDebugInfo : null,
1414+
stack,
1415+
);
14031416
}
14041417
}
14051418
}
@@ -2019,6 +2032,8 @@ function emitAsyncSequence(
20192032
task: Task,
20202033
node: AsyncSequence,
20212034
alreadyForwardedDebugInfo: ?ReactDebugInfo,
2035+
owner: null | ReactComponentInfo,
2036+
stack: null | Error,
20222037
): void {
20232038
const visited: Set<AsyncSequence | ReactDebugInfo> = new Set();
20242039
if (__DEV__ && alreadyForwardedDebugInfo) {
@@ -2034,10 +2049,21 @@ function emitAsyncSequence(
20342049
const env = (0, request.environmentName)();
20352050
// If we don't have any thing awaited, the time we started awaiting was internal
20362051
// when we yielded after rendering. The current task time is basically that.
2037-
emitDebugChunk(request, task.id, {
2052+
const debugInfo: ReactAsyncInfo = {
20382053
awaited: ((awaitedNode: any): ReactIOInfo), // This is deduped by this reference.
20392054
env: env,
2040-
});
2055+
};
2056+
if (__DEV__) {
2057+
if (owner != null) {
2058+
// $FlowFixMe[cannot-write]
2059+
debugInfo.owner = owner;
2060+
}
2061+
if (stack != null) {
2062+
// $FlowFixMe[cannot-write]
2063+
debugInfo.stack = filterStackTrace(request, parseStackTrace(stack, 1));
2064+
}
2065+
}
2066+
emitDebugChunk(request, task.id, debugInfo);
20412067
markOperationEndTime(request, task, awaitedNode.end);
20422068
}
20432069
}
@@ -4316,6 +4342,8 @@ function forwardDebugInfoFromThenable(
43164342
request: Request,
43174343
task: Task,
43184344
thenable: Thenable<any>,
4345+
owner: null | ReactComponentInfo, // DEV-only
4346+
stack: null | Error, // DEV-only
43194347
): void {
43204348
let debugInfo: ?ReactDebugInfo;
43214349
if (__DEV__) {
@@ -4332,7 +4360,7 @@ function forwardDebugInfoFromThenable(
43324360
) {
43334361
const sequence = getAsyncSequenceFromPromise(thenable);
43344362
if (sequence !== null) {
4335-
emitAsyncSequence(request, task, sequence, debugInfo);
4363+
emitAsyncSequence(request, task, sequence, debugInfo, owner, stack);
43364364
}
43374365
}
43384366
}
@@ -4357,7 +4385,7 @@ function forwardDebugInfoFromCurrentContext(
43574385
) {
43584386
const sequence = getCurrentAsyncSequence();
43594387
if (sequence !== null) {
4360-
emitAsyncSequence(request, task, sequence, debugInfo);
4388+
emitAsyncSequence(request, task, sequence, debugInfo, null, null);
43614389
}
43624390
}
43634391
}

packages/react-server/src/ReactFlightThenable.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ import type {
2020
RejectedThenable,
2121
} from 'shared/ReactTypes';
2222

23+
import {enableAsyncDebugInfo} from 'shared/ReactFeatureFlags';
24+
2325
import noop from 'shared/noop';
2426

2527
export type ThenableState = Array<Thenable<any>>;
@@ -50,6 +52,11 @@ export function trackUsedThenable<T>(
5052
const previous = thenableState[index];
5153
if (previous === undefined) {
5254
thenableState.push(thenable);
55+
if (__DEV__ && enableAsyncDebugInfo) {
56+
const stacks: Array<Error> =
57+
(thenableState: any)._stacks || ((thenableState: any)._stacks = []);
58+
stacks.push(new Error());
59+
}
5360
} else {
5461
if (previous !== thenable) {
5562
// Reuse the previous thenable, and drop the new one. We can assume

0 commit comments

Comments
 (0)