Skip to content

Commit 3a60844

Browse files
authored
Update error message for suspending at sync priority (#23361)
Instead of adding a new Suspense boundary, the default recommendation is to wrap the suspending update with startTransition.
1 parent 8d0d0e9 commit 3a60844

File tree

6 files changed

+19
-26
lines changed

6 files changed

+19
-26
lines changed

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

+2-2
Original file line numberDiff line numberDiff line change
@@ -187,7 +187,7 @@ describe('ReactDOMServerSuspense', () => {
187187
1,
188188
);
189189
},
190-
'Add a <Suspense fallback=...> component higher in the tree',
190+
'A component suspended while responding to synchronous input.',
191191
);
192192

193193
itThrowsWhenRendering(
@@ -200,7 +200,7 @@ describe('ReactDOMServerSuspense', () => {
200200
1,
201201
);
202202
},
203-
'Add a <Suspense fallback=...> component higher in the tree',
203+
'A component suspended while responding to synchronous input.',
204204
);
205205
}
206206

packages/react-dom/src/server/ReactDOMLegacyServerBrowser.js

+6-4
Original file line numberDiff line numberDiff line change
@@ -78,11 +78,13 @@ function renderToStringImpl(
7878
}
7979

8080
if (!readyToStream) {
81+
// Note: This error message is the one we use on the client. It doesn't
82+
// really make sense here. But this is the legacy server renderer, anyway.
83+
// We're going to delete it soon.
8184
throw new Error(
82-
'A React component suspended while rendering, but no fallback UI was specified.\n' +
83-
'\n' +
84-
'Add a <Suspense fallback=...> component higher in the tree to ' +
85-
'provide a loading indicator or placeholder to display.',
85+
'A component suspended while responding to synchronous input. This ' +
86+
'will cause the UI to be replaced with a loading indicator. To fix, ' +
87+
'updates that suspend should be wrapped with startTransition.',
8688
);
8789
}
8890

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

+4-8
Original file line numberDiff line numberDiff line change
@@ -499,15 +499,11 @@ function throwException(
499499
// This is a sync/discrete update. We treat this case like an error
500500
// because discrete renders are expected to produce a complete tree
501501
// synchronously to maintain consistency with external state.
502-
503-
// TODO: We should never call getComponentNameFromFiber in production.
504-
// Log a warning or something to prevent us from accidentally bundling it.
505502
const uncaughtSuspenseError = new Error(
506-
(getComponentNameFromFiber(sourceFiber) || 'A React component') +
507-
' suspended while rendering, but no fallback UI was specified.\n' +
508-
'\n' +
509-
'Add a <Suspense fallback=...> component higher in the tree to ' +
510-
'provide a loading indicator or placeholder to display.',
503+
'A component suspended while responding to synchronous input. This ' +
504+
'will cause the UI to be replaced with a loading indicator. To ' +
505+
'fix, updates that suspend should be wrapped ' +
506+
'with startTransition.',
511507
);
512508

513509
// If we're outside a transition, fall through to the regular error path.

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

+4-8
Original file line numberDiff line numberDiff line change
@@ -499,15 +499,11 @@ function throwException(
499499
// This is a sync/discrete update. We treat this case like an error
500500
// because discrete renders are expected to produce a complete tree
501501
// synchronously to maintain consistency with external state.
502-
503-
// TODO: We should never call getComponentNameFromFiber in production.
504-
// Log a warning or something to prevent us from accidentally bundling it.
505502
const uncaughtSuspenseError = new Error(
506-
(getComponentNameFromFiber(sourceFiber) || 'A React component') +
507-
' suspended while rendering, but no fallback UI was specified.\n' +
508-
'\n' +
509-
'Add a <Suspense fallback=...> component higher in the tree to ' +
510-
'provide a loading indicator or placeholder to display.',
503+
'A component suspended while responding to synchronous input. This ' +
504+
'will cause the UI to be replaced with a loading indicator. To ' +
505+
'fix, updates that suspend should be wrapped ' +
506+
'with startTransition.',
511507
);
512508

513509
// If we're outside a transition, fall through to the regular error path.

packages/react-reconciler/src/__tests__/ReactSuspenseWithNoopRenderer-test.js

+1-3
Original file line numberDiff line numberDiff line change
@@ -1011,9 +1011,7 @@ describe('ReactSuspenseWithNoopRenderer', () => {
10111011
ReactNoop.flushSync(() => {
10121012
ReactNoop.render(<AsyncText text="Async" />);
10131013
});
1014-
}).toThrow(
1015-
'AsyncText suspended while rendering, but no fallback UI was specified.',
1016-
);
1014+
}).toThrow('A component suspended while responding to synchronous input.');
10171015
});
10181016

10191017
// @gate enableCache

scripts/error-codes/codes.json

+2-1
Original file line numberDiff line numberDiff line change
@@ -409,5 +409,6 @@
409409
"421": "There was an error while hydrating this Suspense boundary. Switched to client rendering.",
410410
"422": "There was an error while hydrating. Because the error happened outside of a Suspense boundary, the entire root will switch to client rendering.",
411411
"423": "This root received an early update, before anything was able hydrate. Switched the entire root to client rendering.",
412-
"424": "Text content does not match server-rendered HTML."
412+
"424": "Text content does not match server-rendered HTML.",
413+
"425": "A component suspended while responding to synchronous input. This will cause the UI to be replaced with a loading indicator. To fix, updates that suspend should be wrapped with startTransition."
413414
}

0 commit comments

Comments
 (0)