@@ -1105,6 +1105,143 @@ function callWithDebugContextInDEV<A, T>(
11051105
11061106const voidHandler = ( ) => { } ;
11071107
1108+ function processServerComponentReturnValue(
1109+ request: Request,
1110+ task: Task,
1111+ Component: any,
1112+ result: any,
1113+ ): any {
1114+ // A Server Component's return value has a few special properties due to being
1115+ // in the return position of a Component. We convert them here.
1116+ if (
1117+ typeof result !== 'object' ||
1118+ result === null ||
1119+ isClientReference ( result )
1120+ ) {
1121+ return result ;
1122+ }
1123+
1124+ if (typeof result.then === 'function') {
1125+ // When the return value is in children position we can resolve it immediately,
1126+ // to its value without a wrapper if it's synchronously available.
1127+ const thenable : Thenable < any > = result ;
1128+ if ( __DEV__ ) {
1129+ // If the thenable resolves to an element, then it was in a static position,
1130+ // the return value of a Server Component. That doesn't need further validation
1131+ // of keys. The Server Component itself would have had a key.
1132+ thenable. then ( resolvedValue => {
1133+ if (
1134+ typeof resolvedValue === 'object' &&
1135+ resolvedValue !== null &&
1136+ resolvedValue . $$typeof === REACT_ELEMENT_TYPE
1137+ ) {
1138+ resolvedValue . _store . validated = 1 ;
1139+ }
1140+ } , voidHandler ) ;
1141+ }
1142+ if (thenable.status === 'fulfilled') {
1143+ return thenable . value ;
1144+ }
1145+ // TODO: Once we accept Promises as children on the client, we can just return
1146+ // the thenable here.
1147+ return createLazyWrapperAroundWakeable(result);
1148+ }
1149+
1150+ if ( __DEV__ ) {
1151+ if ( ( result : any ) . $$typeof === REACT_ELEMENT_TYPE ) {
1152+ // If the server component renders to an element, then it was in a static position.
1153+ // That doesn't need further validation of keys. The Server Component itself would
1154+ // have had a key.
1155+ ( result : any ) . _store . validated = 1 ;
1156+ }
1157+ }
1158+
1159+ // Normally we'd serialize an Iterator/AsyncIterator as a single-shot which is not compatible
1160+ // to be rendered as a React Child. However, because we have the function to recreate
1161+ // an iterable from rendering the element again, we can effectively treat it as multi-
1162+ // shot. Therefore we treat this as an Iterable/AsyncIterable, whether it was one or not, by
1163+ // adding a wrapper so that this component effectively renders down to an AsyncIterable.
1164+ const iteratorFn = getIteratorFn ( result ) ;
1165+ if ( iteratorFn ) {
1166+ const iterableChild = result ;
1167+ const multiShot = {
1168+ [ Symbol . iterator ] : function ( ) {
1169+ const iterator = iteratorFn . call ( iterableChild ) ;
1170+ if ( __DEV__ ) {
1171+ // If this was an Iterator but not a GeneratorFunction we warn because
1172+ // it might have been a mistake. Technically you can make this mistake with
1173+ // GeneratorFunctions and even single-shot Iterables too but it's extra
1174+ // tempting to try to return the value from a generator.
1175+ if ( iterator === iterableChild ) {
1176+ const isGeneratorComponent =
1177+ // $FlowIgnore[method-unbinding]
1178+ Object . prototype . toString . call ( Component ) ===
1179+ '[object GeneratorFunction]' &&
1180+ // $FlowIgnore[method-unbinding]
1181+ Object . prototype . toString . call ( iterableChild ) ===
1182+ '[object Generator]' ;
1183+ if ( ! isGeneratorComponent ) {
1184+ callWithDebugContextInDEV ( request , task , ( ) => {
1185+ console . error (
1186+ 'Returning an Iterator from a Server Component is not supported ' +
1187+ 'since it cannot be looped over more than once. ' ,
1188+ ) ;
1189+ } ) ;
1190+ }
1191+ }
1192+ }
1193+ return ( iterator : any ) ;
1194+ } ,
1195+ } ;
1196+ if ( __DEV__ ) {
1197+ ( multiShot : any ) . _debugInfo = iterableChild . _debugInfo ;
1198+ }
1199+ return multiShot;
1200+ }
1201+ if (
1202+ enableFlightReadableStream &&
1203+ typeof ( result : any ) [ ASYNC_ITERATOR ] === 'function ' &&
1204+ ( typeof ReadableStream !== 'function ' ||
1205+ ! ( result instanceof ReadableStream ) )
1206+ ) {
1207+ const iterableChild = result ;
1208+ const multishot = {
1209+ [ ASYNC_ITERATOR ] : function ( ) {
1210+ const iterator = ( iterableChild : any ) [ ASYNC_ITERATOR ] ( ) ;
1211+ if ( __DEV__ ) {
1212+ // If this was an AsyncIterator but not an AsyncGeneratorFunction we warn because
1213+ // it might have been a mistake. Technically you can make this mistake with
1214+ // AsyncGeneratorFunctions and even single-shot AsyncIterables too but it's extra
1215+ // tempting to try to return the value from a generator.
1216+ if ( iterator === iterableChild ) {
1217+ const isGeneratorComponent =
1218+ // $FlowIgnore[method-unbinding]
1219+ Object . prototype . toString . call ( Component ) ===
1220+ '[object AsyncGeneratorFunction]' &&
1221+ // $FlowIgnore[method-unbinding]
1222+ Object . prototype . toString . call ( iterableChild ) ===
1223+ '[object AsyncGenerator]' ;
1224+ if ( ! isGeneratorComponent ) {
1225+ callWithDebugContextInDEV ( request , task , ( ) => {
1226+ console . error (
1227+ 'Returning an AsyncIterator from a Server Component is not supported ' +
1228+ 'since it cannot be looped over more than once. ' ,
1229+ ) ;
1230+ } ) ;
1231+ }
1232+ }
1233+ }
1234+ return iterator ;
1235+ } ,
1236+ } ;
1237+ if ( __DEV__ ) {
1238+ ( multishot : any ) . _debugInfo = iterableChild . _debugInfo ;
1239+ }
1240+ return multishot;
1241+ }
1242+ return result ;
1243+ }
1244+
11081245function renderFunctionComponent < Props > (
11091246 request: Request,
11101247 task: Task,
@@ -1231,123 +1368,9 @@ function renderFunctionComponent<Props>(
12311368 throw null;
12321369 }
12331370
1234- if (
1235- typeof result === 'object ' &&
1236- result !== null &&
1237- ! isClientReference ( result )
1238- ) {
1239- if ( typeof result . then === 'function' ) {
1240- // When the return value is in children position we can resolve it immediately,
1241- // to its value without a wrapper if it's synchronously available.
1242- const thenable : Thenable < any > = result ;
1243- if ( __DEV__ ) {
1244- // If the thenable resolves to an element, then it was in a static position,
1245- // the return value of a Server Component. That doesn't need further validation
1246- // of keys. The Server Component itself would have had a key.
1247- thenable. then ( resolvedValue => {
1248- if (
1249- typeof resolvedValue === 'object' &&
1250- resolvedValue !== null &&
1251- resolvedValue . $$typeof === REACT_ELEMENT_TYPE
1252- ) {
1253- resolvedValue . _store . validated = 1 ;
1254- }
1255- } , voidHandler ) ;
1256- }
1257- if (thenable.status === 'fulfilled') {
1258- return thenable . value ;
1259- }
1260- // TODO: Once we accept Promises as children on the client, we can just return
1261- // the thenable here.
1262- result = createLazyWrapperAroundWakeable(result);
1263- }
1371+ // Apply special cases.
1372+ result = processServerComponentReturnValue ( request , task , Component , result ) ;
12641373
1265- // Normally we'd serialize an Iterator/AsyncIterator as a single-shot which is not compatible
1266- // to be rendered as a React Child. However, because we have the function to recreate
1267- // an iterable from rendering the element again, we can effectively treat it as multi-
1268- // shot. Therefore we treat this as an Iterable/AsyncIterable, whether it was one or not, by
1269- // adding a wrapper so that this component effectively renders down to an AsyncIterable.
1270- const iteratorFn = getIteratorFn ( result ) ;
1271- if ( iteratorFn ) {
1272- const iterableChild = result ;
1273- result = {
1274- [ Symbol . iterator ] : function ( ) {
1275- const iterator = iteratorFn . call ( iterableChild ) ;
1276- if ( __DEV__ ) {
1277- // If this was an Iterator but not a GeneratorFunction we warn because
1278- // it might have been a mistake. Technically you can make this mistake with
1279- // GeneratorFunctions and even single-shot Iterables too but it's extra
1280- // tempting to try to return the value from a generator.
1281- if ( iterator === iterableChild ) {
1282- const isGeneratorComponent =
1283- // $FlowIgnore[method-unbinding]
1284- Object . prototype . toString . call ( Component ) ===
1285- '[object GeneratorFunction]' &&
1286- // $FlowIgnore[method-unbinding]
1287- Object . prototype . toString . call ( iterableChild ) ===
1288- '[object Generator]' ;
1289- if ( ! isGeneratorComponent ) {
1290- callWithDebugContextInDEV ( request , task , ( ) => {
1291- console . error (
1292- 'Returning an Iterator from a Server Component is not supported ' +
1293- 'since it cannot be looped over more than once. ' ,
1294- ) ;
1295- } ) ;
1296- }
1297- }
1298- }
1299- return ( iterator : any ) ;
1300- } ,
1301- } ;
1302- if ( __DEV__ ) {
1303- ( result : any ) . _debugInfo = iterableChild . _debugInfo ;
1304- }
1305- } else if (
1306- enableFlightReadableStream &&
1307- typeof ( result : any ) [ ASYNC_ITERATOR ] === 'function ' &&
1308- ( typeof ReadableStream !== 'function ' ||
1309- ! ( result instanceof ReadableStream ) )
1310- ) {
1311- const iterableChild = result ;
1312- result = {
1313- [ ASYNC_ITERATOR ] : function ( ) {
1314- const iterator = ( iterableChild : any ) [ ASYNC_ITERATOR ] ( ) ;
1315- if ( __DEV__ ) {
1316- // If this was an AsyncIterator but not an AsyncGeneratorFunction we warn because
1317- // it might have been a mistake. Technically you can make this mistake with
1318- // AsyncGeneratorFunctions and even single-shot AsyncIterables too but it's extra
1319- // tempting to try to return the value from a generator.
1320- if ( iterator === iterableChild ) {
1321- const isGeneratorComponent =
1322- // $FlowIgnore[method-unbinding]
1323- Object . prototype . toString . call ( Component ) ===
1324- '[object AsyncGeneratorFunction]' &&
1325- // $FlowIgnore[method-unbinding]
1326- Object . prototype . toString . call ( iterableChild ) ===
1327- '[object AsyncGenerator]' ;
1328- if ( ! isGeneratorComponent ) {
1329- callWithDebugContextInDEV ( request , task , ( ) => {
1330- console . error (
1331- 'Returning an AsyncIterator from a Server Component is not supported ' +
1332- 'since it cannot be looped over more than once. ' ,
1333- ) ;
1334- } ) ;
1335- }
1336- }
1337- }
1338- return iterator ;
1339- } ,
1340- } ;
1341- if ( __DEV__ ) {
1342- ( result : any ) . _debugInfo = iterableChild . _debugInfo ;
1343- }
1344- } else if ( __DEV__ && ( result : any ) . $$typeof === REACT_ELEMENT_TYPE ) {
1345- // If the server component renders to an element, then it was in a static position.
1346- // That doesn't need further validation of keys. The Server Component itself would
1347- // have had a key.
1348- ( result : any ) . _store . validated = 1 ;
1349- }
1350- }
13511374 // Track this element's key on the Server Component on the keyPath context..
13521375 const prevKeyPath = task . keyPath ;
13531376 const prevImplicitSlot = task . implicitSlot ;
0 commit comments