Skip to content

Commit 36c245f

Browse files
committed
Special case the error message for generator components
1 parent d1a090e commit 36c245f

File tree

2 files changed

+53
-0
lines changed

2 files changed

+53
-0
lines changed

packages/react-reconciler/src/ReactChildFiber.js

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2129,6 +2129,21 @@ export function validateSuspenseListChildren(
21292129
// TODO: Technically we should warn for nested arrays inside the
21302130
// async iterable but it would require unwrapping the array.
21312131
// However, this mistake is not as easy to make so it's ok not to warn.
2132+
} else if (
2133+
enableAsyncIterableChildren &&
2134+
children.$$typeof === REACT_ELEMENT_TYPE &&
2135+
typeof children.type === 'function' &&
2136+
(Object.prototype.toString.call(children.type) ===
2137+
'[object GeneratorFunction]' ||
2138+
Object.prototype.toString.call(children.type) ===
2139+
'[object AsyncGeneratorFunction]')
2140+
) {
2141+
console.error(
2142+
'A generator Component was passed to a <SuspenseList revealOrder="%s" />. ' +
2143+
'This is not supported as a way to generate lists. Instead, pass an ' +
2144+
'iterable as the children.',
2145+
revealOrder,
2146+
);
21322147
} else {
21332148
console.error(
21342149
'A single row was passed to a <SuspenseList revealOrder="%s" />. ' +

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

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3120,6 +3120,44 @@ describe('ReactSuspenseList', () => {
31203120
},
31213121
);
31223122

3123+
// @gate enableSuspenseList && enableAsyncIterableChildren
3124+
it('warns for async generator components in "forwards" order', async () => {
3125+
async function* Generator() {
3126+
yield 'A';
3127+
yield 'B';
3128+
}
3129+
function Foo() {
3130+
return (
3131+
<SuspenseList revealOrder="forwards">
3132+
<Generator />
3133+
</SuspenseList>
3134+
);
3135+
}
3136+
3137+
await act(() => {
3138+
React.startTransition(() => {
3139+
ReactNoop.render(<Foo />);
3140+
});
3141+
});
3142+
assertConsoleErrorDev([
3143+
'A generator Component was passed to a <SuspenseList revealOrder="forwards" />. ' +
3144+
'This is not supported as a way to generate lists. Instead, pass an ' +
3145+
'iterable as the children.' +
3146+
'\n in SuspenseList (at **)' +
3147+
'\n in Foo (at **)',
3148+
'<Generator> is an async Client Component. ' +
3149+
'Only Server Components can be async at the moment. ' +
3150+
"This error is often caused by accidentally adding `'use client'` " +
3151+
'to a module that was originally written for the server.\n' +
3152+
' in Foo (at **)',
3153+
// We get this warning because the generator's promise themselves are not cached.
3154+
'A component was suspended by an uncached promise. ' +
3155+
'Creating promises inside a Client Component or hook is not yet supported, ' +
3156+
'except via a Suspense-compatible library or framework.\n' +
3157+
' in Foo (at **)',
3158+
]);
3159+
});
3160+
31233161
// @gate enableSuspenseList && enableAsyncIterableChildren
31243162
it('can display async iterable in "forwards" order', async () => {
31253163
const A = createAsyncText('A');

0 commit comments

Comments
 (0)