Skip to content

Commit bd95626

Browse files
committed
[Fizz] Don't pop the replay stack if we've already rendered past an element (#27513)
This is the same problem as we had with keyPath before where if the element itself suspends, we have to restore the replay node to what it was before, however, if something below the element suspends we shouldn't pop it because that will pop it back up the stack. Instead of passing replay as an argument to every renderElement function, I use a hack to compare if the node is still the same as the one we tried to render, then that means we haven't stepped down into the child yet. Maybe this is not quite correct because in theory you could have a recursive node that just renders itself over and over until some context bails out. This solves an issue where if you suspended in an element it would retry trying to replay from that element but using the postponed state from the root. DiffTrain build for [09fbee8](09fbee8)
1 parent 46a8f79 commit bd95626

9 files changed

+275
-227
lines changed

compiled/facebook-www/REVISION

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
bb778528d1ca22b44dad832f0258aaa4c0e6d4a4
1+
09fbee89d62bc1c4a00e64474346cb9bc87682cb

compiled/facebook-www/ReactART-dev.modern.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ function _assertThisInitialized(self) {
6969
return self;
7070
}
7171

72-
var ReactVersion = "18.3.0-www-modern-70e823a6";
72+
var ReactVersion = "18.3.0-www-modern-88adeddd";
7373

7474
var LegacyRoot = 0;
7575
var ConcurrentRoot = 1;

compiled/facebook-www/ReactART-prod.modern.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9780,7 +9780,7 @@ var slice = Array.prototype.slice,
97809780
return null;
97819781
},
97829782
bundleType: 0,
9783-
version: "18.3.0-www-modern-7f660f7a",
9783+
version: "18.3.0-www-modern-0b7b822d",
97849784
rendererPackageName: "react-art"
97859785
};
97869786
var internals$jscomp$inline_1286 = {
@@ -9811,7 +9811,7 @@ var internals$jscomp$inline_1286 = {
98119811
scheduleRoot: null,
98129812
setRefreshHandler: null,
98139813
getCurrentFiber: null,
9814-
reconcilerVersion: "18.3.0-www-modern-7f660f7a"
9814+
reconcilerVersion: "18.3.0-www-modern-0b7b822d"
98159815
};
98169816
if ("undefined" !== typeof __REACT_DEVTOOLS_GLOBAL_HOOK__) {
98179817
var hook$jscomp$inline_1287 = __REACT_DEVTOOLS_GLOBAL_HOOK__;

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

Lines changed: 23 additions & 11 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-c2645119";
22+
var ReactVersion = "18.3.0-www-classic-d0c392db";
2323

2424
// This refers to a WWW module.
2525
var warningWWW = require("warning");
@@ -11437,6 +11437,7 @@ function replayElement(
1143711437

1143811438
var childNodes = node[2];
1143911439
var childSlots = node[3];
11440+
var currentNode = task.node;
1144011441
task.replay = {
1144111442
nodes: childNodes,
1144211443
slots: childSlots,
@@ -11463,25 +11464,33 @@ function replayElement(
1146311464
"The tree doesn't match so React will fallback to client rendering."
1146411465
);
1146511466
}
11467+
11468+
task.replay.pendingTasks--;
1146611469
} catch (x) {
1146711470
if (
1146811471
typeof x === "object" &&
1146911472
x !== null &&
1147011473
(x === SuspenseException || typeof x.then === "function")
1147111474
) {
1147211475
// Suspend
11476+
if (task.node === currentNode) {
11477+
// This same element suspended so we need to pop the replay we just added.
11478+
task.replay = replay;
11479+
}
11480+
1147311481
throw x;
11474-
} // Unlike regular render, we don't terminate the siblings if we error
11482+
}
11483+
11484+
task.replay.pendingTasks--; // Unlike regular render, we don't terminate the siblings if we error
1147511485
// during a replay. That's because this component didn't actually error
1147611486
// in the original prerender. What's unable to complete is the child
1147711487
// replay nodes which might be Suspense boundaries which are able to
1147811488
// absorb the error and we can still continue with siblings.
1147911489

1148011490
erroredReplay(request, task.blockedBoundary, x, childNodes, childSlots);
11481-
} finally {
11482-
task.replay.pendingTasks--;
11483-
task.replay = replay;
1148411491
}
11492+
11493+
task.replay = replay;
1148511494
} else {
1148611495
// Let's double check that the component type matches.
1148711496
if (type !== REACT_SUSPENSE_TYPE) {
@@ -11843,6 +11852,8 @@ function replayFragment(request, task, children, childIndex) {
1184311852
"The tree doesn't match so React will fallback to client rendering."
1184411853
);
1184511854
}
11855+
11856+
task.replay.pendingTasks--;
1184611857
} catch (x) {
1184711858
if (
1184811859
typeof x === "object" &&
@@ -11851,18 +11862,19 @@ function replayFragment(request, task, children, childIndex) {
1185111862
) {
1185211863
// Suspend
1185311864
throw x;
11854-
} // Unlike regular render, we don't terminate the siblings if we error
11865+
}
11866+
11867+
task.replay.pendingTasks--; // Unlike regular render, we don't terminate the siblings if we error
1185511868
// during a replay. That's because this component didn't actually error
1185611869
// in the original prerender. What's unable to complete is the child
1185711870
// replay nodes which might be Suspense boundaries which are able to
1185811871
// absorb the error and we can still continue with siblings.
1185911872
// This is an error, stash the component stack if it is null.
1186011873

1186111874
erroredReplay(request, task.blockedBoundary, x, childNodes, childSlots);
11862-
} finally {
11863-
task.replay.pendingTasks--;
11864-
task.replay = replay;
11865-
} // We finished rendering this node, so now we can consume this
11875+
}
11876+
11877+
task.replay = replay; // We finished rendering this node, so now we can consume this
1186611878
// slot. This must happen after in case we rerender this task.
1186711879

1186811880
replayNodes.splice(j, 1);
@@ -11902,7 +11914,7 @@ function renderChildrenArray(request, task, children, childIndex) {
1190211914
task.treeContext = pushTreeContext(prevTreeContext, totalChildren, i); // We need to use the non-destructive form so that we can safely pop back
1190311915
// up and render the sibling if something suspends.
1190411916

11905-
var resumeSegmentID = resumeSlots[i];
11917+
var resumeSegmentID = resumeSlots[i]; // TODO: If this errors we should still continue with the next sibling.
1190611918

1190711919
if (typeof resumeSegmentID === "number") {
1190811920
resumeNode(request, task, resumeSegmentID, node, i); // We finished rendering this node, so now we can consume this

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

Lines changed: 23 additions & 11 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-modern-70e823a6";
22+
var ReactVersion = "18.3.0-www-modern-88adeddd";
2323

2424
// This refers to a WWW module.
2525
var warningWWW = require("warning");
@@ -11185,6 +11185,7 @@ function replayElement(
1118511185

1118611186
var childNodes = node[2];
1118711187
var childSlots = node[3];
11188+
var currentNode = task.node;
1118811189
task.replay = {
1118911190
nodes: childNodes,
1119011191
slots: childSlots,
@@ -11211,25 +11212,33 @@ function replayElement(
1121111212
"The tree doesn't match so React will fallback to client rendering."
1121211213
);
1121311214
}
11215+
11216+
task.replay.pendingTasks--;
1121411217
} catch (x) {
1121511218
if (
1121611219
typeof x === "object" &&
1121711220
x !== null &&
1121811221
(x === SuspenseException || typeof x.then === "function")
1121911222
) {
1122011223
// Suspend
11224+
if (task.node === currentNode) {
11225+
// This same element suspended so we need to pop the replay we just added.
11226+
task.replay = replay;
11227+
}
11228+
1122111229
throw x;
11222-
} // Unlike regular render, we don't terminate the siblings if we error
11230+
}
11231+
11232+
task.replay.pendingTasks--; // Unlike regular render, we don't terminate the siblings if we error
1122311233
// during a replay. That's because this component didn't actually error
1122411234
// in the original prerender. What's unable to complete is the child
1122511235
// replay nodes which might be Suspense boundaries which are able to
1122611236
// absorb the error and we can still continue with siblings.
1122711237

1122811238
erroredReplay(request, task.blockedBoundary, x, childNodes, childSlots);
11229-
} finally {
11230-
task.replay.pendingTasks--;
11231-
task.replay = replay;
1123211239
}
11240+
11241+
task.replay = replay;
1123311242
} else {
1123411243
// Let's double check that the component type matches.
1123511244
if (type !== REACT_SUSPENSE_TYPE) {
@@ -11591,6 +11600,8 @@ function replayFragment(request, task, children, childIndex) {
1159111600
"The tree doesn't match so React will fallback to client rendering."
1159211601
);
1159311602
}
11603+
11604+
task.replay.pendingTasks--;
1159411605
} catch (x) {
1159511606
if (
1159611607
typeof x === "object" &&
@@ -11599,18 +11610,19 @@ function replayFragment(request, task, children, childIndex) {
1159911610
) {
1160011611
// Suspend
1160111612
throw x;
11602-
} // Unlike regular render, we don't terminate the siblings if we error
11613+
}
11614+
11615+
task.replay.pendingTasks--; // Unlike regular render, we don't terminate the siblings if we error
1160311616
// during a replay. That's because this component didn't actually error
1160411617
// in the original prerender. What's unable to complete is the child
1160511618
// replay nodes which might be Suspense boundaries which are able to
1160611619
// absorb the error and we can still continue with siblings.
1160711620
// This is an error, stash the component stack if it is null.
1160811621

1160911622
erroredReplay(request, task.blockedBoundary, x, childNodes, childSlots);
11610-
} finally {
11611-
task.replay.pendingTasks--;
11612-
task.replay = replay;
11613-
} // We finished rendering this node, so now we can consume this
11623+
}
11624+
11625+
task.replay = replay; // We finished rendering this node, so now we can consume this
1161411626
// slot. This must happen after in case we rerender this task.
1161511627

1161611628
replayNodes.splice(j, 1);
@@ -11650,7 +11662,7 @@ function renderChildrenArray(request, task, children, childIndex) {
1165011662
task.treeContext = pushTreeContext(prevTreeContext, totalChildren, i); // We need to use the non-destructive form so that we can safely pop back
1165111663
// up and render the sibling if something suspends.
1165211664

11653-
var resumeSegmentID = resumeSlots[i];
11665+
var resumeSegmentID = resumeSlots[i]; // TODO: If this errors we should still continue with the next sibling.
1165411666

1165511667
if (typeof resumeSegmentID === "number") {
1165611668
resumeNode(request, task, resumeSegmentID, node, i); // We finished rendering this node, so now we can consume this

0 commit comments

Comments
 (0)