Skip to content

Commit 1d69c7b

Browse files
committed
Create a root task for every Flight response
This lets any element created from the server, to bottom out with a client "owner" which is the creator of the Flight request. This could be a Server Action being invoked or a router. This is similar to how a client element bottoms out in the creator of the root element without an owner. E.g. where the root app element was created. Without this, we inherit the task of whatever is currently executing when we're parsing which can be misleading.
1 parent 9710853 commit 1d69c7b

File tree

1 file changed

+34
-6
lines changed

1 file changed

+34
-6
lines changed

packages/react-client/src/ReactFlightClient.js

Lines changed: 34 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -254,6 +254,7 @@ export type Response = {
254254
_rowLength: number, // remaining bytes in the row. 0 indicates that we're looking for a newline.
255255
_buffer: Array<Uint8Array>, // chunks received so far as part of this row
256256
_tempRefs: void | TemporaryReferenceSet, // the set temporary references can be resolved from
257+
_debugRootTask?: null | ConsoleTask, // DEV-only
257258
};
258259

259260
function readChunk<T>(chunk: SomeChunk<T>): T {
@@ -614,6 +615,7 @@ function getTaskName(type: mixed): string {
614615
}
615616

616617
function createElement(
618+
response: Response,
617619
type: mixed,
618620
key: mixed,
619621
props: mixed,
@@ -697,9 +699,15 @@ function createElement(
697699
const callStack = buildFakeCallStack(stack, createTaskFn);
698700
// This owner should ideally have already been initialized to avoid getting
699701
// user stack frames on the stack.
700-
const ownerTask = owner === null ? null : initializeFakeTask(owner);
702+
const ownerTask =
703+
owner === null ? null : initializeFakeTask(response, owner);
701704
if (ownerTask === null) {
702-
task = callStack();
705+
const rootTask = response._debugRootTask;
706+
if (rootTask != null) {
707+
task = rootTask.run(callStack);
708+
} else {
709+
task = callStack();
710+
}
703711
} else {
704712
task = ownerTask.run(callStack);
705713
}
@@ -1106,6 +1114,7 @@ function parseModelTuple(
11061114
// TODO: Consider having React just directly accept these arrays as elements.
11071115
// Or even change the ReactElement type to be an array.
11081116
return createElement(
1117+
response,
11091118
tuple[1],
11101119
tuple[2],
11111120
tuple[3],
@@ -1149,6 +1158,14 @@ export function createResponse(
11491158
_buffer: [],
11501159
_tempRefs: temporaryReferences,
11511160
};
1161+
if (supportsCreateTask) {
1162+
// Any stacks that appear on the server need to be rooted somehow on the client
1163+
// so we create a root Task for this response which will be the root owner for any
1164+
// elements created by the server. We use the "use server" string to indicate that
1165+
// this is where we enter the server from the client.
1166+
// TODO: Make this string configurable.
1167+
response._debugRootTask = (console: any).createTask('"use server"');
1168+
}
11521169
// Don't inline this call because it causes closure to outline the call above.
11531170
response._fromJSON = createFromJSONCallback(response);
11541171
return response;
@@ -1730,6 +1747,7 @@ function buildFakeCallStack<T>(stack: string, innerCall: () => T): () => T {
17301747
}
17311748

17321749
function initializeFakeTask(
1750+
response: Response,
17331751
debugInfo: ReactComponentInfo | ReactAsyncInfo,
17341752
): null | ConsoleTask {
17351753
if (taskCache === null || typeof debugInfo.stack !== 'string') {
@@ -1745,7 +1763,7 @@ function initializeFakeTask(
17451763
const ownerTask =
17461764
componentInfo.owner == null
17471765
? null
1748-
: initializeFakeTask(componentInfo.owner);
1766+
: initializeFakeTask(response, componentInfo.owner);
17491767

17501768
// eslint-disable-next-line react-internal/no-production-logging
17511769
const createTaskFn = (console: any).createTask.bind(
@@ -1755,7 +1773,12 @@ function initializeFakeTask(
17551773
const callStack = buildFakeCallStack(stack, createTaskFn);
17561774

17571775
if (ownerTask === null) {
1758-
return callStack();
1776+
const rootTask = response._debugRootTask;
1777+
if (rootTask != null) {
1778+
return rootTask.run(callStack);
1779+
} else {
1780+
return callStack();
1781+
}
17591782
} else {
17601783
return ownerTask.run(callStack);
17611784
}
@@ -1776,7 +1799,7 @@ function resolveDebugInfo(
17761799
// We eagerly initialize the fake task because this resolving happens outside any
17771800
// render phase so we're not inside a user space stack at this point. If we waited
17781801
// to initialize it when we need it, we might be inside user code.
1779-
initializeFakeTask(debugInfo);
1802+
initializeFakeTask(response, debugInfo);
17801803
const chunk = getChunk(response, id);
17811804
const chunkDebugInfo: ReactDebugInfo =
17821805
chunk._debugInfo || (chunk._debugInfo = []);
@@ -1813,12 +1836,17 @@ function resolveConsoleEntry(
18131836
printToConsole.bind(null, methodName, args, env),
18141837
);
18151838
if (owner != null) {
1816-
const task = initializeFakeTask(owner);
1839+
const task = initializeFakeTask(response, owner);
18171840
if (task !== null) {
18181841
task.run(callStack);
18191842
return;
18201843
}
18211844
}
1845+
const rootTask = response._debugRootTask;
1846+
if (rootTask != null) {
1847+
rootTask.run(callStack);
1848+
return;
1849+
}
18221850
callStack();
18231851
}
18241852

0 commit comments

Comments
 (0)