Skip to content

Commit ab3288b

Browse files
committed
[Fizz] Track Key Path (#27243)
Tracks the currently executing parent path of a task, using the name of the component and the key or index. This can be used to uniquely identify an instance of a component between requests - assuming nothing in the parents has changed. Even if it has changed, if things are properly keyed, it should still line up. It's not used yet but we'll need this for two separate features so should land this so we can stack on top. Can be passed to `JSON.stringify(...)` to generate a unique key. DiffTrain build for [98f3f14](98f3f14)
1 parent 0fe197b commit ab3288b

File tree

7 files changed

+620
-308
lines changed

7 files changed

+620
-308
lines changed

compiled/facebook-www/REVISION

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
e50531692010bbda2a4627b07c7c810c3770a52a
1+
98f3f14d2e06fb785103296318204cd154d5d0ed

compiled/facebook-www/ReactDOMServer-dev.classic.js

Lines changed: 72 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ if (__DEV__) {
1919
var React = require("react");
2020
var ReactDOM = require("react-dom");
2121

22-
var ReactVersion = "18.3.0-www-classic-f7f9d058";
22+
var ReactVersion = "18.3.0-www-classic-5c145eb3";
2323

2424
// This refers to a WWW module.
2525
var warningWWW = require("warning");
@@ -9692,7 +9692,10 @@ function getStackByComponentStackNode(componentStack) {
96929692

96939693
var ReactCurrentDispatcher = ReactSharedInternals.ReactCurrentDispatcher;
96949694
var ReactCurrentCache = ReactSharedInternals.ReactCurrentCache;
9695-
var ReactDebugCurrentFrame = ReactSharedInternals.ReactDebugCurrentFrame;
9695+
var ReactDebugCurrentFrame = ReactSharedInternals.ReactDebugCurrentFrame; // Linked list representing the identity of a component given the component/tag name and key.
9696+
// The name might be minified but we assume that it's going to be the same generated name. Typically
9697+
// because it's just the same compiled output in practice.
9698+
96969699
var PENDING = 0;
96979700
var COMPLETED = 1;
96989701
var FLUSHED = 2;
@@ -9787,6 +9790,7 @@ function createRequest(
97879790
null,
97889791
rootSegment,
97899792
abortSet,
9793+
null,
97909794
emptyContextObject,
97919795
rootContextSnapshot,
97929796
emptyTreeContext
@@ -9835,6 +9839,7 @@ function createTask(
98359839
blockedBoundary,
98369840
blockedSegment,
98379841
abortSet,
9842+
keyPath,
98389843
legacyContext,
98399844
context,
98409845
treeContext
@@ -9855,6 +9860,7 @@ function createTask(
98559860
blockedBoundary: blockedBoundary,
98569861
blockedSegment: blockedSegment,
98579862
abortSet: abortSet,
9863+
keyPath: keyPath,
98589864
legacyContext: legacyContext,
98599865
context: context,
98609866
treeContext: treeContext,
@@ -10060,7 +10066,7 @@ function renderSuspenseBoundary(request, task, props) {
1006010066

1006110067
try {
1006210068
// We use the safe form because we don't handle suspending here. Only error handling.
10063-
renderNode(request, task, content);
10069+
renderNode(request, task, content, 0);
1006410070
pushSegmentFinale(
1006510071
contentRootSegment.chunks,
1006610072
request.responseState,
@@ -10113,6 +10119,7 @@ function renderSuspenseBoundary(request, task, props) {
1011310119
parentBoundary,
1011410120
boundarySegment,
1011510121
fallbackAbortSet,
10122+
task.keyPath,
1011610123
task.legacyContext,
1011710124
task.context,
1011810125
task.treeContext
@@ -10144,7 +10151,7 @@ function renderHostElement(request, task, type, props) {
1014410151
segment.formatContext = getChildFormatContext(prevContext, type, props); // We use the non-destructive form because if something suspends, we still
1014510152
// need to pop back up and finish this subtree of HTML.
1014610153

10147-
renderNode(request, task, children); // We expect that errors will fatal the whole task and that we don't need
10154+
renderNode(request, task, children, 0); // We expect that errors will fatal the whole task and that we don't need
1014810155
// the correct context. Therefore this is not in a finally.
1014910156

1015010157
segment.formatContext = prevContext;
@@ -10206,13 +10213,13 @@ function finishClassComponent(request, task, instance, Component, props) {
1020610213
childContextTypes
1020710214
);
1020810215
task.legacyContext = mergedContext;
10209-
renderNodeDestructive(request, task, null, nextChildren);
10216+
renderNodeDestructive(request, task, null, nextChildren, 0);
1021010217
task.legacyContext = previousContext;
1021110218
return;
1021210219
}
1021310220
}
1021410221

10215-
renderNodeDestructive(request, task, null, nextChildren);
10222+
renderNodeDestructive(request, task, null, nextChildren, 0);
1021610223
}
1021710224

1021810225
function renderClassComponent(request, task, Component, props) {
@@ -10323,12 +10330,12 @@ function renderIndeterminateComponent(
1032310330
task.treeContext = pushTreeContext(prevTreeContext, totalChildren, index);
1032410331

1032510332
try {
10326-
renderNodeDestructive(request, task, null, value);
10333+
renderNodeDestructive(request, task, null, value, 0);
1032710334
} finally {
1032810335
task.treeContext = prevTreeContext;
1032910336
}
1033010337
} else {
10331-
renderNodeDestructive(request, task, null, value);
10338+
renderNodeDestructive(request, task, null, value, 0);
1033210339
}
1033310340
}
1033410341

@@ -10430,12 +10437,12 @@ function renderForwardRef(request, task, prevThenableState, type, props, ref) {
1043010437
task.treeContext = pushTreeContext(prevTreeContext, totalChildren, index);
1043110438

1043210439
try {
10433-
renderNodeDestructive(request, task, null, children);
10440+
renderNodeDestructive(request, task, null, children, 0);
1043410441
} finally {
1043510442
task.treeContext = prevTreeContext;
1043610443
}
1043710444
} else {
10438-
renderNodeDestructive(request, task, null, children);
10445+
renderNodeDestructive(request, task, null, children, 0);
1043910446
}
1044010447

1044110448
popComponentStackInDEV(task);
@@ -10497,7 +10504,7 @@ function renderContextConsumer(request, task, context, props) {
1049710504

1049810505
var newValue = readContext$1(context);
1049910506
var newChildren = render(newValue);
10500-
renderNodeDestructive(request, task, null, newChildren);
10507+
renderNodeDestructive(request, task, null, newChildren, 0);
1050110508
}
1050210509

1050310510
function renderContextProvider(request, task, type, props) {
@@ -10511,7 +10518,7 @@ function renderContextProvider(request, task, type, props) {
1051110518
}
1051210519

1051310520
task.context = pushProvider(context, value);
10514-
renderNodeDestructive(request, task, null, children);
10521+
renderNodeDestructive(request, task, null, children, 0);
1051510522
task.context = popProvider(context);
1051610523

1051710524
{
@@ -10554,7 +10561,7 @@ function renderOffscreen(request, task, props) {
1055410561
else {
1055510562
// A visible Offscreen boundary is treated exactly like a fragment: a
1055610563
// pure indirection.
10557-
renderNodeDestructive(request, task, null, props.children);
10564+
renderNodeDestructive(request, task, null, props.children, 0);
1055810565
}
1055910566
}
1056010567

@@ -10595,7 +10602,7 @@ function renderElement(request, task, prevThenableState, type, props, ref) {
1059510602
case REACT_STRICT_MODE_TYPE:
1059610603
case REACT_PROFILER_TYPE:
1059710604
case REACT_FRAGMENT_TYPE: {
10598-
renderNodeDestructive(request, task, null, props.children);
10605+
renderNodeDestructive(request, task, null, props.children, 0);
1059910606
return;
1060010607
}
1060110608

@@ -10607,14 +10614,14 @@ function renderElement(request, task, prevThenableState, type, props, ref) {
1060710614
case REACT_SUSPENSE_LIST_TYPE: {
1060810615
pushBuiltInComponentStackInDEV(task, "SuspenseList"); // TODO: SuspenseList should control the boundaries.
1060910616

10610-
renderNodeDestructive(request, task, null, props.children);
10617+
renderNodeDestructive(request, task, null, props.children, 0);
1061110618
popComponentStackInDEV(task);
1061210619
return;
1061310620
}
1061410621

1061510622
case REACT_SCOPE_TYPE: {
1061610623
{
10617-
renderNodeDestructive(request, task, null, props.children);
10624+
renderNodeDestructive(request, task, null, props.children, 0);
1061810625
return;
1061910626
}
1062010627
}
@@ -10719,14 +10726,21 @@ function renderNodeDestructive(
1071910726
task, // The thenable state reused from the previous attempt, if any. This is almost
1072010727
// always null, except when called by retryTask.
1072110728
prevThenableState,
10722-
node
10729+
node,
10730+
childIndex
1072310731
) {
1072410732
{
1072510733
// In Dev we wrap renderNodeDestructiveImpl in a try / catch so we can capture
1072610734
// a component stack at the right place in the tree. We don't do this in renderNode
1072710735
// becuase it is not called at every layer of the tree and we may lose frames
1072810736
try {
10729-
return renderNodeDestructiveImpl(request, task, prevThenableState, node);
10737+
return renderNodeDestructiveImpl(
10738+
request,
10739+
task,
10740+
prevThenableState,
10741+
node,
10742+
childIndex
10743+
);
1073010744
} catch (x) {
1073110745
if (typeof x === "object" && x !== null && typeof x.then === "function");
1073210746
else {
@@ -10743,7 +10757,13 @@ function renderNodeDestructive(
1074310757
} // This function by it self renders a node and consumes the task by mutating it
1074410758
// to update the current execution state.
1074510759

10746-
function renderNodeDestructiveImpl(request, task, prevThenableState, node) {
10760+
function renderNodeDestructiveImpl(
10761+
request,
10762+
task,
10763+
prevThenableState,
10764+
node,
10765+
childIndex
10766+
) {
1074710767
// Stash the node we're working on. We'll pick up from this task in case
1074810768
// something suspends.
1074910769
task.node = node; // Handle object types
@@ -10753,9 +10773,14 @@ function renderNodeDestructiveImpl(request, task, prevThenableState, node) {
1075310773
case REACT_ELEMENT_TYPE: {
1075410774
var element = node;
1075510775
var type = element.type;
10776+
var key = element.key;
1075610777
var props = element.props;
1075710778
var ref = element.ref;
10779+
var name = getComponentNameFromType(type);
10780+
var prevKeyPath = task.keyPath;
10781+
task.keyPath = [task.keyPath, name, key == null ? childIndex : key];
1075810782
renderElement(request, task, prevThenableState, type, props, ref);
10783+
task.keyPath = prevKeyPath;
1075910784
return;
1076010785
}
1076110786

@@ -10791,13 +10816,13 @@ function renderNodeDestructiveImpl(request, task, prevThenableState, node) {
1079110816
}
1079210817
}
1079310818

10794-
renderNodeDestructive(request, task, null, resolvedNode);
10819+
renderNodeDestructive(request, task, null, resolvedNode, childIndex);
1079510820
return;
1079610821
}
1079710822
}
1079810823

1079910824
if (isArray(node)) {
10800-
renderChildrenArray(request, task, node);
10825+
renderChildrenArray(request, task, node, childIndex);
1080110826
return;
1080210827
}
1080310828

@@ -10826,7 +10851,7 @@ function renderNodeDestructiveImpl(request, task, prevThenableState, node) {
1082610851
step = iterator.next();
1082710852
} while (!step.done);
1082810853

10829-
renderChildrenArray(request, task, children);
10854+
renderChildrenArray(request, task, children, childIndex);
1083010855
return;
1083110856
}
1083210857

@@ -10850,7 +10875,8 @@ function renderNodeDestructiveImpl(request, task, prevThenableState, node) {
1085010875
request,
1085110876
task,
1085210877
null,
10853-
unwrapThenable(thenable)
10878+
unwrapThenable(thenable),
10879+
childIndex
1085410880
);
1085510881
}
1085610882

@@ -10863,7 +10889,8 @@ function renderNodeDestructiveImpl(request, task, prevThenableState, node) {
1086310889
request,
1086410890
task,
1086510891
null,
10866-
readContext$1(context)
10892+
readContext$1(context),
10893+
childIndex
1086710894
);
1086810895
} // $FlowFixMe[method-unbinding]
1086910896

@@ -10912,19 +10939,28 @@ function renderNodeDestructiveImpl(request, task, prevThenableState, node) {
1091210939
}
1091310940
}
1091410941

10915-
function renderChildrenArray(request, task, children) {
10942+
function renderChildrenArray(request, task, children, childIndex) {
10943+
var prevKeyPath = task.keyPath;
1091610944
var totalChildren = children.length;
1091710945

1091810946
for (var i = 0; i < totalChildren; i++) {
1091910947
var prevTreeContext = task.treeContext;
1092010948
task.treeContext = pushTreeContext(prevTreeContext, totalChildren, i);
1092110949

1092210950
try {
10923-
// We need to use the non-destructive form so that we can safely pop back
10951+
var node = children[i];
10952+
10953+
if (isArray(node) || getIteratorFn(node)) {
10954+
// Nested arrays behave like a "fragment node" which is keyed.
10955+
// Therefore we need to add the current index as a parent key.
10956+
task.keyPath = [task.keyPath, "", childIndex];
10957+
} // We need to use the non-destructive form so that we can safely pop back
1092410958
// up and render the sibling if something suspends.
10925-
renderNode(request, task, children[i]);
10959+
10960+
renderNode(request, task, node, i);
1092610961
} finally {
1092710962
task.treeContext = prevTreeContext;
10963+
task.keyPath = prevKeyPath;
1092810964
}
1092910965
}
1093010966
}
@@ -10951,6 +10987,7 @@ function spawnNewSuspendedTask(request, task, thenableState, x) {
1095110987
task.blockedBoundary,
1095210988
newSegment,
1095310989
task.abortSet,
10990+
task.keyPath,
1095410991
task.legacyContext,
1095510992
task.context,
1095610993
task.treeContext
@@ -10969,7 +11006,7 @@ function spawnNewSuspendedTask(request, task, thenableState, x) {
1096911006
} // This is a non-destructive form of rendering a node. If it suspends it spawns
1097011007
// a new task and restores the context of this task to what it was before.
1097111008

10972-
function renderNode(request, task, node) {
11009+
function renderNode(request, task, node, childIndex) {
1097311010
// Store how much we've pushed at this point so we can reset it in case something
1097411011
// suspended partially through writing something.
1097511012
var segment = task.blockedSegment;
@@ -10980,14 +11017,15 @@ function renderNode(request, task, node) {
1098011017
var previousFormatContext = task.blockedSegment.formatContext;
1098111018
var previousLegacyContext = task.legacyContext;
1098211019
var previousContext = task.context;
11020+
var previousKeyPath = task.keyPath;
1098311021
var previousComponentStack = null;
1098411022

1098511023
{
1098611024
previousComponentStack = task.componentStack;
1098711025
}
1098811026

1098911027
try {
10990-
return renderNodeDestructive(request, task, null, node);
11028+
return renderNodeDestructive(request, task, null, node, childIndex);
1099111029
} catch (thrownValue) {
1099211030
resetHooksState(); // Reset the write pointers to where we started.
1099311031

@@ -11010,7 +11048,8 @@ function renderNode(request, task, node) {
1101011048

1101111049
task.blockedSegment.formatContext = previousFormatContext;
1101211050
task.legacyContext = previousLegacyContext;
11013-
task.context = previousContext; // Restore all active ReactContexts to what they were before.
11051+
task.context = previousContext;
11052+
task.keyPath = previousKeyPath; // Restore all active ReactContexts to what they were before.
1101411053

1101511054
switchContext(previousContext);
1101611055

@@ -11024,7 +11063,8 @@ function renderNode(request, task, node) {
1102411063
// functions in case nothing throws so we don't use "finally" here.
1102511064
task.blockedSegment.formatContext = previousFormatContext;
1102611065
task.legacyContext = previousLegacyContext;
11027-
task.context = previousContext; // Restore all active ReactContexts to what they were before.
11066+
task.context = previousContext;
11067+
task.keyPath = previousKeyPath; // Restore all active ReactContexts to what they were before.
1102811068

1102911069
switchContext(previousContext);
1103011070

@@ -11286,7 +11326,7 @@ function retryTask(request, task) {
1128611326
// component suspends again, the thenable state will be restored.
1128711327
var prevThenableState = task.thenableState;
1128811328
task.thenableState = null;
11289-
renderNodeDestructive(request, task, prevThenableState, task.node);
11329+
renderNodeDestructive(request, task, prevThenableState, task.node, 0);
1129011330
pushSegmentFinale(
1129111331
segment.chunks,
1129211332
request.responseState,

0 commit comments

Comments
 (0)