@@ -277,6 +277,29 @@ describe('ReactFlight', () => {
277277 expect ( ReactNoop ) . toMatchRenderedOutput ( < span > ABC</ span > ) ;
278278 } ) ;
279279
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+
280303 it ( 'can render undefined' , async ( ) => {
281304 function Undefined ( ) {
282305 return undefined ;
@@ -2133,16 +2156,9 @@ describe('ReactFlight', () => {
21332156 }
21342157 const Stateful = clientReference ( StatefulClient ) ;
21352158
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 } /> ;
21462162 }
21472163
21482164 function ListClient ( { children} ) {
@@ -2154,6 +2170,11 @@ describe('ReactFlight', () => {
21542170 expect ( fragment . type ) . toBe ( React . Fragment ) ;
21552171 const fragmentChildren = [ ] ;
21562172 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+ }
21572178 for ( let entry ; ! ( entry = React . use ( iterator . next ( ) ) ) . done ; ) {
21582179 fragmentChildren . push ( entry . value ) ;
21592180 }
@@ -2298,23 +2319,21 @@ describe('ReactFlight', () => {
22982319 let resolve ;
22992320 const iteratorPromise = new Promise ( r => ( resolve = r ) ) ;
23002321
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 ( ) ;
23122326 }
23132327
23142328 function ListClient ( { children : fragment } ) {
23152329 // TODO: Unwrap AsyncIterables natively in React. For now we do it in this wrapper.
23162330 const resolvedChildren = [ ] ;
23172331 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+ }
23182337 for ( let entry ; ! ( entry = React . use ( iterator . next ( ) ) ) . done ; ) {
23192338 resolvedChildren . push ( entry . value ) ;
23202339 }
0 commit comments