@@ -277,6 +277,29 @@ describe('ReactFlight', () => {
277
277
expect ( ReactNoop ) . toMatchRenderedOutput ( < span > ABC</ span > ) ;
278
278
} ) ;
279
279
280
+ it ( 'can render a Generator Server Component as a fragment' , async ( ) => {
281
+ function ItemListClient ( props ) {
282
+ return < span > { props . children } </ span > ;
283
+ }
284
+ const ItemList = clientReference ( ItemListClient ) ;
285
+
286
+ function * Items ( ) {
287
+ yield 'A' ;
288
+ yield 'B' ;
289
+ yield 'C' ;
290
+ }
291
+
292
+ const model = < ItemList > < Items /> </ ItemList > ;
293
+
294
+ const transport = ReactNoopFlightServer . render ( model ) ;
295
+
296
+ await act ( async ( ) => {
297
+ ReactNoop . render ( await ReactNoopFlightClient . read ( transport ) ) ;
298
+ } ) ;
299
+
300
+ expect ( ReactNoop ) . toMatchRenderedOutput ( < span > ABC</ span > ) ;
301
+ } ) ;
302
+
280
303
it ( 'can render undefined' , async ( ) => {
281
304
function Undefined ( ) {
282
305
return undefined ;
@@ -2133,16 +2156,9 @@ describe('ReactFlight', () => {
2133
2156
}
2134
2157
const Stateful = clientReference ( StatefulClient ) ;
2135
2158
2136
- function ServerComponent ( { item, initial} ) {
2137
- // While the ServerComponent itself could be an async generator, single-shot iterables
2138
- // are not supported as React children since React might need to re-map them based on
2139
- // state updates. So we create an AsyncIterable instead.
2140
- return {
2141
- async * [ Symbol . asyncIterator ] ( ) {
2142
- yield < Stateful key = "a" initial = { 'a' + initial } /> ;
2143
- yield < Stateful key = "b" initial = { 'b' + initial } /> ;
2144
- } ,
2145
- } ;
2159
+ async function * ServerComponent ( { item, initial} ) {
2160
+ yield < Stateful key = "a" initial = { 'a' + initial } /> ;
2161
+ yield < Stateful key = "b" initial = { 'b' + initial } /> ;
2146
2162
}
2147
2163
2148
2164
function ListClient ( { children} ) {
@@ -2154,6 +2170,11 @@ describe('ReactFlight', () => {
2154
2170
expect ( fragment . type ) . toBe ( React . Fragment ) ;
2155
2171
const fragmentChildren = [ ] ;
2156
2172
const iterator = fragment . props . children [ Symbol . asyncIterator ] ( ) ;
2173
+ if ( iterator === fragment . props . children ) {
2174
+ console . error (
2175
+ 'AyncIterators are not valid children of React. It must be a multi-shot AsyncIterable.' ,
2176
+ ) ;
2177
+ }
2157
2178
for ( let entry ; ! ( entry = React . use ( iterator . next ( ) ) ) . done ; ) {
2158
2179
fragmentChildren . push ( entry . value ) ;
2159
2180
}
@@ -2298,23 +2319,21 @@ describe('ReactFlight', () => {
2298
2319
let resolve ;
2299
2320
const iteratorPromise = new Promise ( r => ( resolve = r ) ) ;
2300
2321
2301
- function ThirdPartyAsyncIterableComponent ( { item, initial} ) {
2302
- // While the ServerComponent itself could be an async generator, single-shot iterables
2303
- // are not supported as React children since React might need to re-map them based on
2304
- // state updates. So we create an AsyncIterable instead.
2305
- return {
2306
- async * [ Symbol . asyncIterator ] ( ) {
2307
- yield < span > Who</ span > ;
2308
- yield < span > dis?</ span > ;
2309
- resolve ( ) ;
2310
- } ,
2311
- } ;
2322
+ async function * ThirdPartyAsyncIterableComponent ( { item, initial} ) {
2323
+ yield < span > Who</ span > ;
2324
+ yield < span > dis?</ span > ;
2325
+ resolve ( ) ;
2312
2326
}
2313
2327
2314
2328
function ListClient ( { children : fragment } ) {
2315
2329
// TODO: Unwrap AsyncIterables natively in React. For now we do it in this wrapper.
2316
2330
const resolvedChildren = [ ] ;
2317
2331
const iterator = fragment . props . children [ Symbol . asyncIterator ] ( ) ;
2332
+ if ( iterator === fragment . props . children ) {
2333
+ console . error (
2334
+ 'AyncIterators are not valid children of React. It must be a multi-shot AsyncIterable.' ,
2335
+ ) ;
2336
+ }
2318
2337
for ( let entry ; ! ( entry = React . use ( iterator . next ( ) ) ) . done ; ) {
2319
2338
resolvedChildren . push ( entry . value ) ;
2320
2339
}
0 commit comments