Skip to content

Commit 2157c76

Browse files
committed
Remove recoverable error when a sync update flows into a dehydrated boundary
1 parent d1e35c7 commit 2157c76

File tree

3 files changed

+18
-70
lines changed

3 files changed

+18
-70
lines changed

packages/react-dom/src/__tests__/ReactDOMServerPartialHydration-test.internal.js

Lines changed: 0 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -453,12 +453,6 @@ describe('ReactDOMServerPartialHydration', () => {
453453

454454
Scheduler.unstable_flushAll();
455455
jest.runAllTimers();
456-
expect(Scheduler).toHaveYielded([
457-
'This Suspense boundary received an update before it finished ' +
458-
'hydrating. This caused the boundary to switch to client rendering. ' +
459-
'The usual way to fix this is to wrap the original update ' +
460-
'in startTransition.',
461-
]);
462456

463457
expect(hydrated.length).toBe(1);
464458
expect(deleted.length).toBe(1);
@@ -1102,12 +1096,6 @@ describe('ReactDOMServerPartialHydration', () => {
11021096
root.render(<App text="Hi" className="hi" />);
11031097
Scheduler.unstable_flushAll();
11041098
jest.runAllTimers();
1105-
expect(Scheduler).toHaveYielded([
1106-
'This Suspense boundary received an update before it finished ' +
1107-
'hydrating. This caused the boundary to switch to client ' +
1108-
'rendering. The usual way to fix this is to wrap the original ' +
1109-
'update in startTransition.',
1110-
]);
11111099

11121100
// Flushing now should delete the existing content and show the fallback.
11131101

@@ -1191,12 +1179,6 @@ describe('ReactDOMServerPartialHydration', () => {
11911179
// Flushing now should delete the existing content and show the fallback.
11921180
Scheduler.unstable_flushAll();
11931181
jest.runAllTimers();
1194-
expect(Scheduler).toHaveYielded([
1195-
'This Suspense boundary received an update before it finished ' +
1196-
'hydrating. This caused the boundary to switch to client rendering. ' +
1197-
'The usual way to fix this is to wrap the original update ' +
1198-
'in startTransition.',
1199-
]);
12001182

12011183
expect(container.getElementsByTagName('span').length).toBe(1);
12021184
expect(ref.current).toBe(span);
@@ -1284,12 +1266,6 @@ describe('ReactDOMServerPartialHydration', () => {
12841266
suspend = false;
12851267
resolve();
12861268
await promise;
1287-
expect(Scheduler).toHaveYielded([
1288-
'This Suspense boundary received an update before it finished ' +
1289-
'hydrating. This caused the boundary to switch to client rendering. ' +
1290-
'The usual way to fix this is to wrap the original update ' +
1291-
'in startTransition.',
1292-
]);
12931269

12941270
Scheduler.unstable_flushAll();
12951271
jest.runAllTimers();
@@ -1602,12 +1578,6 @@ describe('ReactDOMServerPartialHydration', () => {
16021578
// Flushing now should delete the existing content and show the fallback.
16031579
Scheduler.unstable_flushAll();
16041580
jest.runAllTimers();
1605-
expect(Scheduler).toHaveYielded([
1606-
'This Suspense boundary received an update before it finished ' +
1607-
'hydrating. This caused the boundary to switch to client rendering. ' +
1608-
'The usual way to fix this is to wrap the original update ' +
1609-
'in startTransition.',
1610-
]);
16111581

16121582
expect(container.getElementsByTagName('span').length).toBe(0);
16131583
expect(ref.current).toBe(null);
@@ -2360,12 +2330,6 @@ describe('ReactDOMServerPartialHydration', () => {
23602330
// This will force all expiration times to flush.
23612331
Scheduler.unstable_flushAll();
23622332
jest.runAllTimers();
2363-
expect(Scheduler).toHaveYielded([
2364-
'This Suspense boundary received an update before it finished ' +
2365-
'hydrating. This caused the boundary to switch to client rendering. ' +
2366-
'The usual way to fix this is to wrap the original update ' +
2367-
'in startTransition.',
2368-
]);
23692333

23702334
// This will now be a new span because we weren't able to hydrate before
23712335
const newSpan = container.getElementsByTagName('span')[0];

packages/react-reconciler/src/ReactFiberBeginWork.new.js

Lines changed: 9 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -2717,9 +2717,6 @@ function updateDehydratedSuspenseComponent(
27172717
current,
27182718
workInProgress,
27192719
renderLanes,
2720-
// TODO: When we delete legacy mode, we should make this error argument
2721-
// required — every concurrent mode path that causes hydration to
2722-
// de-opt to client rendering should have an error message.
27232720
null,
27242721
);
27252722
}
@@ -2809,25 +2806,20 @@ function updateDehydratedSuspenseComponent(
28092806
}
28102807
}
28112808

2812-
// If we have scheduled higher pri work above, this will probably just abort the render
2813-
// since we now have higher priority work, but in case it doesn't, we need to prepare to
2814-
// render something, if we time out. Even if that requires us to delete everything and
2815-
// skip hydration.
2816-
// Delay having to do this as long as the suspense timeout allows us.
2809+
// If we have scheduled higher pri work above, this will just abort the render
2810+
// since we now have higher priority work. We'll try to infinitely suspend until
2811+
// we yield. TODO: We could probably just force yielding earlier instead.
28172812
renderDidSuspendDelayIfPossible();
2818-
const capturedValue = createCapturedValue(
2819-
new Error(
2820-
'This Suspense boundary received an update before it finished ' +
2821-
'hydrating. This caused the boundary to switch to client rendering. ' +
2822-
'The usual way to fix this is to wrap the original update ' +
2823-
'in startTransition.',
2824-
),
2825-
);
2813+
// If we rendered synchronously, we won't yield so have to render something.
2814+
// This will cause us to delete any existing content.
2815+
// TODO: We should ideally have a sync hydration lane that we can apply to do
2816+
// a pass where we hydrate this subtree in place using the previous Context and then
2817+
// reapply the update afterwards.
28262818
return retrySuspenseComponentWithoutHydrating(
28272819
current,
28282820
workInProgress,
28292821
renderLanes,
2830-
capturedValue,
2822+
null,
28312823
);
28322824
} else if (isSuspenseInstancePending(suspenseInstance)) {
28332825
// This component is still pending more data from the server, so we can't hydrate its

packages/react-reconciler/src/ReactFiberBeginWork.old.js

Lines changed: 9 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -2717,9 +2717,6 @@ function updateDehydratedSuspenseComponent(
27172717
current,
27182718
workInProgress,
27192719
renderLanes,
2720-
// TODO: When we delete legacy mode, we should make this error argument
2721-
// required — every concurrent mode path that causes hydration to
2722-
// de-opt to client rendering should have an error message.
27232720
null,
27242721
);
27252722
}
@@ -2809,25 +2806,20 @@ function updateDehydratedSuspenseComponent(
28092806
}
28102807
}
28112808

2812-
// If we have scheduled higher pri work above, this will probably just abort the render
2813-
// since we now have higher priority work, but in case it doesn't, we need to prepare to
2814-
// render something, if we time out. Even if that requires us to delete everything and
2815-
// skip hydration.
2816-
// Delay having to do this as long as the suspense timeout allows us.
2809+
// If we have scheduled higher pri work above, this will just abort the render
2810+
// since we now have higher priority work. We'll try to infinitely suspend until
2811+
// we yield. TODO: We could probably just force yielding earlier instead.
28172812
renderDidSuspendDelayIfPossible();
2818-
const capturedValue = createCapturedValue(
2819-
new Error(
2820-
'This Suspense boundary received an update before it finished ' +
2821-
'hydrating. This caused the boundary to switch to client rendering. ' +
2822-
'The usual way to fix this is to wrap the original update ' +
2823-
'in startTransition.',
2824-
),
2825-
);
2813+
// If we rendered synchronously, we won't yield so have to render something.
2814+
// This will cause us to delete any existing content.
2815+
// TODO: We should ideally have a sync hydration lane that we can apply to do
2816+
// a pass where we hydrate this subtree in place using the previous Context and then
2817+
// reapply the update afterwards.
28262818
return retrySuspenseComponentWithoutHydrating(
28272819
current,
28282820
workInProgress,
28292821
renderLanes,
2830-
capturedValue,
2822+
null,
28312823
);
28322824
} else if (isSuspenseInstancePending(suspenseInstance)) {
28332825
// This component is still pending more data from the server, so we can't hydrate its

0 commit comments

Comments
 (0)