Skip to content

Commit 382190c

Browse files
authored
[Flight/Fizz] Reset ThenableState Only in Branches Where It's Added (#28068)
Before, we used to reset the thenable state and extract the previous state very early so that it's only the retried task that can possibly consume it. This is nice because we can't accidentally consume that state for any other node. However, it does add a lot of branches of code that has to pass this around. It also adds extra bytes on the stack per node. Even though it's mostly just null. This changes it so that where ever we can create a thenable state (e.g. entering a component with hooks) we first extract this from the task. The principle is that whatever could've created the thenable state in the first place, must always be rerendered so it'll take the same code paths to get there and so we'll always consume it.
1 parent c7e735f commit 382190c

File tree

2 files changed

+48
-155
lines changed

2 files changed

+48
-155
lines changed

packages/react-server/src/ReactFizzServer.js

Lines changed: 29 additions & 115 deletions
Original file line numberDiff line numberDiff line change
@@ -1294,11 +1294,15 @@ function renderWithHooks<Props, SecondArg>(
12941294
request: Request,
12951295
task: Task,
12961296
keyPath: KeyNode,
1297-
prevThenableState: ThenableState | null,
12981297
Component: (p: Props, arg: SecondArg) => any,
12991298
props: Props,
13001299
secondArg: SecondArg,
13011300
): any {
1301+
// Reset the task's thenable state before continuing, so that if a later
1302+
// component suspends we can reuse the same task object. If the same
1303+
// component suspends again, the thenable state will be restored.
1304+
const prevThenableState = task.thenableState;
1305+
task.thenableState = null;
13021306
const componentIdentity = {};
13031307
prepareToUseHooks(
13041308
request,
@@ -1345,15 +1349,15 @@ function finishClassComponent(
13451349
childContextTypes,
13461350
);
13471351
task.legacyContext = mergedContext;
1348-
renderNodeDestructive(request, task, null, nextChildren, -1);
1352+
renderNodeDestructive(request, task, nextChildren, -1);
13491353
task.legacyContext = previousContext;
13501354
return;
13511355
}
13521356
}
13531357

13541358
const prevKeyPath = task.keyPath;
13551359
task.keyPath = keyPath;
1356-
renderNodeDestructive(request, task, null, nextChildren, -1);
1360+
renderNodeDestructive(request, task, nextChildren, -1);
13571361
task.keyPath = prevKeyPath;
13581362
}
13591363

@@ -1391,7 +1395,6 @@ function renderIndeterminateComponent(
13911395
request: Request,
13921396
task: Task,
13931397
keyPath: KeyNode,
1394-
prevThenableState: ThenableState | null,
13951398
Component: any,
13961399
props: any,
13971400
): void {
@@ -1425,7 +1428,6 @@ function renderIndeterminateComponent(
14251428
request,
14261429
task,
14271430
keyPath,
1428-
prevThenableState,
14291431
Component,
14301432
props,
14311433
legacyContext,
@@ -1569,7 +1571,7 @@ function finishFunctionComponent(
15691571
// We're now successfully past this task, and we haven't modified the
15701572
// context stack. We don't have to pop back to the previous task every
15711573
// again, so we can use the destructive recursive form.
1572-
renderNodeDestructive(request, task, null, children, -1);
1574+
renderNodeDestructive(request, task, children, -1);
15731575
}
15741576
task.keyPath = prevKeyPath;
15751577
}
@@ -1646,7 +1648,6 @@ function renderForwardRef(
16461648
request: Request,
16471649
task: Task,
16481650
keyPath: KeyNode,
1649-
prevThenableState: null | ThenableState,
16501651
type: any,
16511652
props: Object,
16521653
ref: any,
@@ -1657,7 +1658,6 @@ function renderForwardRef(
16571658
request,
16581659
task,
16591660
keyPath,
1660-
prevThenableState,
16611661
type.render,
16621662
props,
16631663
ref,
@@ -1681,22 +1681,13 @@ function renderMemo(
16811681
request: Request,
16821682
task: Task,
16831683
keyPath: KeyNode,
1684-
prevThenableState: ThenableState | null,
16851684
type: any,
16861685
props: Object,
16871686
ref: any,
16881687
): void {
16891688
const innerType = type.type;
16901689
const resolvedProps = resolveDefaultProps(innerType, props);
1691-
renderElement(
1692-
request,
1693-
task,
1694-
keyPath,
1695-
prevThenableState,
1696-
innerType,
1697-
resolvedProps,
1698-
ref,
1699-
);
1690+
renderElement(request, task, keyPath, innerType, resolvedProps, ref);
17001691
}
17011692

17021693
function renderContextConsumer(
@@ -1749,7 +1740,7 @@ function renderContextConsumer(
17491740

17501741
const prevKeyPath = task.keyPath;
17511742
task.keyPath = keyPath;
1752-
renderNodeDestructive(request, task, null, newChildren, -1);
1743+
renderNodeDestructive(request, task, newChildren, -1);
17531744
task.keyPath = prevKeyPath;
17541745
}
17551746

@@ -1770,7 +1761,7 @@ function renderContextProvider(
17701761
const prevKeyPath = task.keyPath;
17711762
task.context = pushProvider(context, value);
17721763
task.keyPath = keyPath;
1773-
renderNodeDestructive(request, task, null, children, -1);
1764+
renderNodeDestructive(request, task, children, -1);
17741765
task.context = popProvider(context);
17751766
task.keyPath = prevKeyPath;
17761767
if (__DEV__) {
@@ -1786,7 +1777,6 @@ function renderLazyComponent(
17861777
request: Request,
17871778
task: Task,
17881779
keyPath: KeyNode,
1789-
prevThenableState: ThenableState | null,
17901780
lazyComponent: LazyComponentType<any, any>,
17911781
props: Object,
17921782
ref: any,
@@ -1797,15 +1787,7 @@ function renderLazyComponent(
17971787
const init = lazyComponent._init;
17981788
const Component = init(payload);
17991789
const resolvedProps = resolveDefaultProps(Component, props);
1800-
renderElement(
1801-
request,
1802-
task,
1803-
keyPath,
1804-
prevThenableState,
1805-
Component,
1806-
resolvedProps,
1807-
ref,
1808-
);
1790+
renderElement(request, task, keyPath, Component, resolvedProps, ref);
18091791
task.componentStack = previousComponentStack;
18101792
}
18111793

@@ -1824,7 +1806,7 @@ function renderOffscreen(
18241806
// pure indirection.
18251807
const prevKeyPath = task.keyPath;
18261808
task.keyPath = keyPath;
1827-
renderNodeDestructive(request, task, null, props.children, -1);
1809+
renderNodeDestructive(request, task, props.children, -1);
18281810
task.keyPath = prevKeyPath;
18291811
}
18301812
}
@@ -1833,7 +1815,6 @@ function renderElement(
18331815
request: Request,
18341816
task: Task,
18351817
keyPath: KeyNode,
1836-
prevThenableState: ThenableState | null,
18371818
type: any,
18381819
props: Object,
18391820
ref: any,
@@ -1843,14 +1824,7 @@ function renderElement(
18431824
renderClassComponent(request, task, keyPath, type, props);
18441825
return;
18451826
} else {
1846-
renderIndeterminateComponent(
1847-
request,
1848-
task,
1849-
keyPath,
1850-
prevThenableState,
1851-
type,
1852-
props,
1853-
);
1827+
renderIndeterminateComponent(request, task, keyPath, type, props);
18541828
return;
18551829
}
18561830
}
@@ -1876,7 +1850,7 @@ function renderElement(
18761850
case REACT_FRAGMENT_TYPE: {
18771851
const prevKeyPath = task.keyPath;
18781852
task.keyPath = keyPath;
1879-
renderNodeDestructive(request, task, null, props.children, -1);
1853+
renderNodeDestructive(request, task, props.children, -1);
18801854
task.keyPath = prevKeyPath;
18811855
return;
18821856
}
@@ -1890,7 +1864,7 @@ function renderElement(
18901864
// TODO: SuspenseList should control the boundaries.
18911865
const prevKeyPath = task.keyPath;
18921866
task.keyPath = keyPath;
1893-
renderNodeDestructive(request, task, null, props.children, -1);
1867+
renderNodeDestructive(request, task, props.children, -1);
18941868
task.keyPath = prevKeyPath;
18951869
task.componentStack = preiousComponentStack;
18961870
return;
@@ -1899,7 +1873,7 @@ function renderElement(
18991873
if (enableScopeAPI) {
19001874
const prevKeyPath = task.keyPath;
19011875
task.keyPath = keyPath;
1902-
renderNodeDestructive(request, task, null, props.children, -1);
1876+
renderNodeDestructive(request, task, props.children, -1);
19031877
task.keyPath = prevKeyPath;
19041878
return;
19051879
}
@@ -1921,19 +1895,11 @@ function renderElement(
19211895
if (typeof type === 'object' && type !== null) {
19221896
switch (type.$$typeof) {
19231897
case REACT_FORWARD_REF_TYPE: {
1924-
renderForwardRef(
1925-
request,
1926-
task,
1927-
keyPath,
1928-
prevThenableState,
1929-
type,
1930-
props,
1931-
ref,
1932-
);
1898+
renderForwardRef(request, task, keyPath, type, props, ref);
19331899
return;
19341900
}
19351901
case REACT_MEMO_TYPE: {
1936-
renderMemo(request, task, keyPath, prevThenableState, type, props, ref);
1902+
renderMemo(request, task, keyPath, type, props, ref);
19371903
return;
19381904
}
19391905
case REACT_PROVIDER_TYPE: {
@@ -1945,14 +1911,7 @@ function renderElement(
19451911
return;
19461912
}
19471913
case REACT_LAZY_TYPE: {
1948-
renderLazyComponent(
1949-
request,
1950-
task,
1951-
keyPath,
1952-
prevThenableState,
1953-
type,
1954-
props,
1955-
);
1914+
renderLazyComponent(request, task, keyPath, type, props);
19561915
return;
19571916
}
19581917
}
@@ -2025,7 +1984,6 @@ function replayElement(
20251984
request: Request,
20261985
task: ReplayTask,
20271986
keyPath: KeyNode,
2028-
prevThenableState: ThenableState | null,
20291987
name: null | string,
20301988
keyOrIndex: number | string,
20311989
childIndex: number,
@@ -2060,15 +2018,7 @@ function replayElement(
20602018
const currentNode = task.node;
20612019
task.replay = {nodes: childNodes, slots: childSlots, pendingTasks: 1};
20622020
try {
2063-
renderElement(
2064-
request,
2065-
task,
2066-
keyPath,
2067-
prevThenableState,
2068-
type,
2069-
props,
2070-
ref,
2071-
);
2021+
renderElement(request, task, keyPath, type, props, ref);
20722022
if (
20732023
task.replay.pendingTasks === 1 &&
20742024
task.replay.nodes.length > 0
@@ -2184,9 +2134,6 @@ function validateIterable(iterable, iteratorFn: Function): void {
21842134
function renderNodeDestructive(
21852135
request: Request,
21862136
task: Task,
2187-
// The thenable state reused from the previous attempt, if any. This is almost
2188-
// always null, except when called by retryTask.
2189-
prevThenableState: ThenableState | null,
21902137
node: ReactNodeList,
21912138
childIndex: number,
21922139
): void {
@@ -2223,7 +2170,6 @@ function renderNodeDestructive(
22232170
request,
22242171
task,
22252172
keyPath,
2226-
prevThenableState,
22272173
name,
22282174
keyOrIndex,
22292175
childIndex,
@@ -2236,15 +2182,7 @@ function renderNodeDestructive(
22362182
// prelude and skip it during the replay.
22372183
} else {
22382184
// We're doing a plain render.
2239-
renderElement(
2240-
request,
2241-
task,
2242-
keyPath,
2243-
prevThenableState,
2244-
type,
2245-
props,
2246-
ref,
2247-
);
2185+
renderElement(request, task, keyPath, type, props, ref);
22482186
}
22492187
return;
22502188
}
@@ -2266,7 +2204,7 @@ function renderNodeDestructive(
22662204
task.componentStack = previousComponentStack;
22672205

22682206
// Now we render the resolved node
2269-
renderNodeDestructive(request, task, null, resolvedNode, childIndex);
2207+
renderNodeDestructive(request, task, resolvedNode, childIndex);
22702208
return;
22712209
}
22722210
}
@@ -2314,11 +2252,12 @@ function renderNodeDestructive(
23142252
// e.g. Usable<Usable<Usable<T>>> should resolve to T
23152253
const maybeUsable: Object = node;
23162254
if (typeof maybeUsable.then === 'function') {
2255+
// Clear any previous thenable state that was created by the unwrapping.
2256+
task.thenableState = null;
23172257
const thenable: Thenable<ReactNodeList> = (maybeUsable: any);
23182258
return renderNodeDestructive(
23192259
request,
23202260
task,
2321-
null,
23222261
unwrapThenable(thenable),
23232262
childIndex,
23242263
);
@@ -2332,7 +2271,6 @@ function renderNodeDestructive(
23322271
return renderNodeDestructive(
23332272
request,
23342273
task,
2335-
null,
23362274
readContext(context),
23372275
childIndex,
23382276
);
@@ -2827,7 +2765,7 @@ function renderNode(
28272765
if (segment === null) {
28282766
// Replay
28292767
try {
2830-
return renderNodeDestructive(request, task, null, node, childIndex);
2768+
return renderNodeDestructive(request, task, node, childIndex);
28312769
} catch (thrownValue) {
28322770
resetHooksState();
28332771

@@ -2875,7 +2813,7 @@ function renderNode(
28752813
const childrenLength = segment.children.length;
28762814
const chunkLength = segment.chunks.length;
28772815
try {
2878-
return renderNodeDestructive(request, task, null, node, childIndex);
2816+
return renderNodeDestructive(request, task, node, childIndex);
28792817
} catch (thrownValue) {
28802818
resetHooksState();
28812819

@@ -3456,19 +3394,7 @@ function retryRenderTask(
34563394
// We call the destructive form that mutates this task. That way if something
34573395
// suspends again, we can reuse the same task instead of spawning a new one.
34583396

3459-
// Reset the task's thenable state before continuing, so that if a later
3460-
// component suspends we can reuse the same task object. If the same
3461-
// component suspends again, the thenable state will be restored.
3462-
const prevThenableState = task.thenableState;
3463-
task.thenableState = null;
3464-
3465-
renderNodeDestructive(
3466-
request,
3467-
task,
3468-
prevThenableState,
3469-
task.node,
3470-
task.childIndex,
3471-
);
3397+
renderNodeDestructive(request, task, task.node, task.childIndex);
34723398
pushSegmentFinale(
34733399
segment.chunks,
34743400
request.renderState,
@@ -3559,19 +3485,7 @@ function retryReplayTask(request: Request, task: ReplayTask): void {
35593485
// We call the destructive form that mutates this task. That way if something
35603486
// suspends again, we can reuse the same task instead of spawning a new one.
35613487

3562-
// Reset the task's thenable state before continuing, so that if a later
3563-
// component suspends we can reuse the same task object. If the same
3564-
// component suspends again, the thenable state will be restored.
3565-
const prevThenableState = task.thenableState;
3566-
task.thenableState = null;
3567-
3568-
renderNodeDestructive(
3569-
request,
3570-
task,
3571-
prevThenableState,
3572-
task.node,
3573-
task.childIndex,
3574-
);
3488+
renderNodeDestructive(request, task, task.node, task.childIndex);
35753489

35763490
if (task.replay.pendingTasks === 1 && task.replay.nodes.length > 0) {
35773491
throw new Error(

0 commit comments

Comments
 (0)