Skip to content

Commit 15b04ea

Browse files
committed
Ensure the temporary corruption of internal state goes unnoticed
1 parent 0f0adcf commit 15b04ea

File tree

2 files changed

+19
-11
lines changed

2 files changed

+19
-11
lines changed

packages/react-server-dom-webpack/src/__tests__/ReactFlightDOMNode-test.js

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -809,6 +809,7 @@ describe('ReactFlightDOMNode', () => {
809809
resolvePendingPromise = value => {
810810
p2.status = 'fulfilled';
811811
p2.value = value;
812+
resolve(value);
812813
};
813814
});
814815
const p3 = new Promise(() => {});
@@ -888,11 +889,11 @@ describe('ReactFlightDOMNode', () => {
888889
},
889890
);
890891

892+
resolvePendingPromise('custom-instrum-resolve');
891893
await serverAct(
892894
async () =>
893895
new Promise(resolve => {
894896
setImmediate(() => {
895-
resolvePendingPromise();
896897
clientAbortController.abort();
897898
resolve();
898899
});
@@ -908,7 +909,7 @@ describe('ReactFlightDOMNode', () => {
908909
expect(normalizeCodeLocInfo(componentStack)).toBe(
909910
'\n' +
910911
' in SharedComponent (at **)\n' +
911-
' in ServerComponent\n' +
912+
' in ServerComponent (at **)\n' +
912913
' in Suspense\n' +
913914
' in body\n' +
914915
' in html\n' +
@@ -932,8 +933,8 @@ describe('ReactFlightDOMNode', () => {
932933
// The concrete location may change as this test is updated.
933934
// Just make sure they still point at React.use(p2)
934935
'\n at SharedComponent (./ReactFlightDOMNode-test.js:794:7)' +
935-
'\n at ServerComponent (file://./ReactFlightDOMNode-test.js:815:26)' +
936-
'\n at App (file://./ReactFlightDOMNode-test.js:832:25)',
936+
'\n at ServerComponent (file://./ReactFlightDOMNode-test.js:816:26)' +
937+
'\n at App (file://./ReactFlightDOMNode-test.js:833:25)',
937938
);
938939
} else {
939940
expect(ownerStack).toBeNull();
@@ -1524,12 +1525,12 @@ describe('ReactFlightDOMNode', () => {
15241525
'\n' +
15251526
' in Dynamic' +
15261527
(gate(flags => flags.enableAsyncDebugInfo)
1527-
? ' (file://ReactFlightDOMNode-test.js:1394:27)\n'
1528+
? ' (file://ReactFlightDOMNode-test.js:1395:27)\n'
15281529
: '\n') +
15291530
' in body\n' +
15301531
' in html\n' +
1531-
' in App (file://ReactFlightDOMNode-test.js:1411:25)\n' +
1532-
' in ClientRoot (ReactFlightDOMNode-test.js:1486:16)',
1532+
' in App (file://ReactFlightDOMNode-test.js:1412:25)\n' +
1533+
' in ClientRoot (ReactFlightDOMNode-test.js:1487:16)',
15331534
);
15341535
} else {
15351536
expect(
@@ -1538,7 +1539,7 @@ describe('ReactFlightDOMNode', () => {
15381539
'\n' +
15391540
' in body\n' +
15401541
' in html\n' +
1541-
' in ClientRoot (ReactFlightDOMNode-test.js:1486:16)',
1542+
' in ClientRoot (ReactFlightDOMNode-test.js:1487:16)',
15421543
);
15431544
}
15441545

@@ -1548,16 +1549,16 @@ describe('ReactFlightDOMNode', () => {
15481549
normalizeCodeLocInfo(ownerStack, {preserveLocation: true}),
15491550
).toBe(
15501551
'\n' +
1551-
' in Dynamic (file://ReactFlightDOMNode-test.js:1394:27)\n' +
1552-
' in App (file://ReactFlightDOMNode-test.js:1411:25)',
1552+
' in Dynamic (file://ReactFlightDOMNode-test.js:1395:27)\n' +
1553+
' in App (file://ReactFlightDOMNode-test.js:1412:25)',
15531554
);
15541555
} else {
15551556
expect(
15561557
normalizeCodeLocInfo(ownerStack, {preserveLocation: true}),
15571558
).toBe(
15581559
'' +
15591560
'\n' +
1560-
' in App (file://ReactFlightDOMNode-test.js:1411:25)',
1561+
' in App (file://ReactFlightDOMNode-test.js:1412:25)',
15611562
);
15621563
}
15631564
} else {

packages/react-server/src/ReactFizzThenable.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -255,12 +255,19 @@ export function ensureSuspendableThenableStateDEV(
255255
): () => void {
256256
if (__DEV__) {
257257
const lastThenable = thenableState[thenableState.length - 1];
258+
// Reset the last thenable back to pending.
258259
switch (lastThenable.status) {
259260
case 'fulfilled':
260261
const previousThenableValue = lastThenable.value;
262+
const previousThenableThen = lastThenable.then;
261263
delete lastThenable.value;
262264
delete (lastThenable: any).status;
265+
// We'll call .then again if we resuspend. Since we potentially corrupted
266+
// the internal state of unknown classes, we need to diffuse the potential
267+
// crash by replacing the .then method with a noop.
268+
lastThenable.then = noop;
263269
return () => {
270+
lastThenable.then = previousThenableThen.bind(lastThenable);
264271
lastThenable.value = previousThenableValue;
265272
lastThenable.status = 'fulfilled';
266273
};

0 commit comments

Comments
 (0)