Commit 3d2ab01

Browse files
authored
[Flight] Extract special cases for Server Component return value position (#31713)
This is just moving some code into a helper. We have a bunch of special cases for the return value slot of a Server Component that's different from just rendering that inside an object. This was getting a little tricky to reason about inline with the rest of rendering.
1 parent 76d603a commit 3d2ab01

1 file changed

Lines changed: 139 additions & 116 deletions

File tree

‎packages/react-server/src/ReactFlightServer.js‎

Lines changed: 139 additions & 116 deletions
Original file line numberDiff line numberDiff line change
@@ -1105,6 +1105,143 @@ function callWithDebugContextInDEV<A, T>(
11051105

11061106
constvoidHandler=()=>{};
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+
typeofresult!=='object'||
1118+
result===null||
1119+
isClientReference(result)
1120+
){
1121+
returnresult;
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+
constthenable: 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+
typeofresolvedValue==='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+
returnthenable.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+
constiteratorFn=getIteratorFn(result);
1165+
if(iteratorFn){
1166+
constiterableChild=result;
1167+
constmultiShot={
1168+
[Symbol.iterator]: function(){
1169+
constiterator=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+
constisGeneratorComponent=
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+
(typeofReadableStream!== 'function' ||
1205+
!(resultinstanceofReadableStream))
1206+
){
1207+
constiterableChild=result;
1208+
constmultishot={
1209+
[ASYNC_ITERATOR]: function(){
1210+
constiterator=(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+
constisGeneratorComponent=
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+
returniterator;
1235+
},
1236+
};
1237+
if(__DEV__){
1238+
(multishot: any)._debugInfo=iterableChild._debugInfo;
1239+
}
1240+
return multishot;
1241+
}
1242+
returnresult;
1243+
}
1244+
11081245
functionrenderFunctionComponent<Props>(
11091246
request: Request,
11101247
task: Task,
@@ -1231,123 +1368,9 @@ function renderFunctionComponent<Props>(
12311368
throw null;
12321369
}
12331370

1234-
if(
1235-
typeofresult=== 'object' &&
1236-
result!==null&&
1237-
!isClientReference(result)
1238-
){
1239-
if(typeofresult.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-
constthenable: 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-
typeofresolvedValue==='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-
returnthenable.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-
constiteratorFn=getIteratorFn(result);
1271-
if(iteratorFn){
1272-
constiterableChild=result;
1273-
result={
1274-
[Symbol.iterator]: function(){
1275-
constiterator=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-
constisGeneratorComponent=
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-
}elseif(
1306-
enableFlightReadableStream&&
1307-
typeof(result: any)[ASYNC_ITERATOR]=== 'function' &&
1308-
(typeofReadableStream!== 'function' ||
1309-
!(resultinstanceofReadableStream))
1310-
){
1311-
constiterableChild=result;
1312-
result={
1313-
[ASYNC_ITERATOR]: function(){
1314-
constiterator=(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-
constisGeneratorComponent=
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-
returniterator;
1339-
},
1340-
};
1341-
if(__DEV__){
1342-
(result: any)._debugInfo=iterableChild._debugInfo;
1343-
}
1344-
}elseif(__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
constprevKeyPath=task.keyPath;
13531376
constprevImplicitSlot=task.implicitSlot;

0 commit comments

Comments
 (0)
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

Commit 3d2ab01

Browse files
authored
[Flight] Extract special cases for Server Component return value position (#31713)
This is just moving some code into a helper. We have a bunch of special cases for the return value slot of a Server Component that's different from just rendering that inside an object. This was getting a little tricky to reason about inline with the rest of rendering.
1 parent 76d603a commit 3d2ab01

1 file changed

Lines changed: 139 additions & 116 deletions

File tree

‎packages/react-server/src/ReactFlightServer.js‎

Lines changed: 139 additions & 116 deletions
Original file line numberDiff line numberDiff line change
@@ -1105,6 +1105,143 @@ function callWithDebugContextInDEV<A, T>(
11051105

11061106
constvoidHandler=()=>{};
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+
typeofresult!=='object'||
1118+
result===null||
1119+
isClientReference(result)
1120+
){
1121+
returnresult;
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+
constthenable: 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+
typeofresolvedValue==='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+
returnthenable.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+
constiteratorFn=getIteratorFn(result);
1165+
if(iteratorFn){
1166+
constiterableChild=result;
1167+
constmultiShot={
1168+
[Symbol.iterator]: function(){
1169+
constiterator=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+
constisGeneratorComponent=
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+
(typeofReadableStream!== 'function' ||
1205+
!(resultinstanceofReadableStream))
1206+
){
1207+
constiterableChild=result;
1208+
constmultishot={
1209+
[ASYNC_ITERATOR]: function(){
1210+
constiterator=(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+
constisGeneratorComponent=
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+
returniterator;
1235+
},
1236+
};
1237+
if(__DEV__){
1238+
(multishot: any)._debugInfo=iterableChild._debugInfo;
1239+
}
1240+
return multishot;
1241+
}
1242+
returnresult;
1243+
}
1244+
11081245
functionrenderFunctionComponent<Props>(
11091246
request: Request,
11101247
task: Task,
@@ -1231,123 +1368,9 @@ function renderFunctionComponent<Props>(
12311368
throw null;
12321369
}
12331370

1234-
if(
1235-
typeofresult=== 'object' &&
1236-
result!==null&&
1237-
!isClientReference(result)
1238-
){
1239-
if(typeofresult.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-
constthenable: 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-
typeofresolvedValue==='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-
returnthenable.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-
constiteratorFn=getIteratorFn(result);
1271-
if(iteratorFn){
1272-
constiterableChild=result;
1273-
result={
1274-
[Symbol.iterator]: function(){
1275-
constiterator=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-
constisGeneratorComponent=
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-
}elseif(
1306-
enableFlightReadableStream&&
1307-
typeof(result: any)[ASYNC_ITERATOR]=== 'function' &&
1308-
(typeofReadableStream!== 'function' ||
1309-
!(resultinstanceofReadableStream))
1310-
){
1311-
constiterableChild=result;
1312-
result={
1313-
[ASYNC_ITERATOR]: function(){
1314-
constiterator=(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-
constisGeneratorComponent=
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-
returniterator;
1339-
},
1340-
};
1341-
if(__DEV__){
1342-
(result: any)._debugInfo=iterableChild._debugInfo;
1343-
}
1344-
}elseif(__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
constprevKeyPath=task.keyPath;
13531376
constprevImplicitSlot=task.implicitSlot;

0 commit comments

Comments
 (0)
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Commit 3d2ab01

Browse files
authored
[Flight] Extract special cases for Server Component return value position (#31713)
This is just moving some code into a helper. We have a bunch of special cases for the return value slot of a Server Component that's different from just rendering that inside an object. This was getting a little tricky to reason about inline with the rest of rendering.
1 parent 76d603a commit 3d2ab01

1 file changed

Lines changed: 139 additions & 116 deletions

File tree

‎packages/react-server/src/ReactFlightServer.js‎

Lines changed: 139 additions & 116 deletions
Original file line numberDiff line numberDiff line change
@@ -1105,6 +1105,143 @@ function callWithDebugContextInDEV<A, T>(
11051105

11061106
constvoidHandler=()=>{};
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+
typeofresult!=='object'||
1118+
result===null||
1119+
isClientReference(result)
1120+
){
1121+
returnresult;
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+
constthenable: 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+
typeofresolvedValue==='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+
returnthenable.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+
constiteratorFn=getIteratorFn(result);
1165+
if(iteratorFn){
1166+
constiterableChild=result;
1167+
constmultiShot={
1168+
[Symbol.iterator]: function(){
1169+
constiterator=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+
constisGeneratorComponent=
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+
(typeofReadableStream!== 'function' ||
1205+
!(resultinstanceofReadableStream))
1206+
){
1207+
constiterableChild=result;
1208+
constmultishot={
1209+
[ASYNC_ITERATOR]: function(){
1210+
constiterator=(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+
constisGeneratorComponent=
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+
returniterator;
1235+
},
1236+
};
1237+
if(__DEV__){
1238+
(multishot: any)._debugInfo=iterableChild._debugInfo;
1239+
}
1240+
return multishot;
1241+
}
1242+
returnresult;
1243+
}
1244+
11081245
functionrenderFunctionComponent<Props>(
11091246
request: Request,
11101247
task: Task,
@@ -1231,123 +1368,9 @@ function renderFunctionComponent<Props>(
12311368
throw null;
12321369
}
12331370

1234-
if(
1235-
typeofresult=== 'object' &&
1236-
result!==null&&
1237-
!isClientReference(result)
1238-
){
1239-
if(typeofresult.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-
constthenable: 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-
typeofresolvedValue==='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-
returnthenable.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-
constiteratorFn=getIteratorFn(result);
1271-
if(iteratorFn){
1272-
constiterableChild=result;
1273-
result={
1274-
[Symbol.iterator]: function(){
1275-
constiterator=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-
constisGeneratorComponent=
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-
}elseif(
1306-
enableFlightReadableStream&&
1307-
typeof(result: any)[ASYNC_ITERATOR]=== 'function' &&
1308-
(typeofReadableStream!== 'function' ||
1309-
!(resultinstanceofReadableStream))
1310-
){
1311-
constiterableChild=result;
1312-
result={
1313-
[ASYNC_ITERATOR]: function(){
1314-
constiterator=(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-
constisGeneratorComponent=
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-
returniterator;
1339-
},
1340-
};
1341-
if(__DEV__){
1342-
(result: any)._debugInfo=iterableChild._debugInfo;
1343-
}
1344-
}elseif(__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
constprevKeyPath=task.keyPath;
13531376
constprevImplicitSlot=task.implicitSlot;

0 commit comments

Comments
 (0)
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Commit 3d2ab01

Browse files
authored
[Flight] Extract special cases for Server Component return value position (#31713)
This is just moving some code into a helper. We have a bunch of special cases for the return value slot of a Server Component that's different from just rendering that inside an object. This was getting a little tricky to reason about inline with the rest of rendering.
1 parent 76d603a commit 3d2ab01

1 file changed

Lines changed: 139 additions & 116 deletions

File tree

‎packages/react-server/src/ReactFlightServer.js‎

Lines changed: 139 additions & 116 deletions
Original file line numberDiff line numberDiff line change
@@ -1105,6 +1105,143 @@ function callWithDebugContextInDEV<A, T>(
11051105

11061106
constvoidHandler=()=>{};
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+
typeofresult!=='object'||
1118+
result===null||
1119+
isClientReference(result)
1120+
){
1121+
returnresult;
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+
constthenable: 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+
typeofresolvedValue==='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+
returnthenable.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+
constiteratorFn=getIteratorFn(result);
1165+
if(iteratorFn){
1166+
constiterableChild=result;
1167+
constmultiShot={
1168+
[Symbol.iterator]: function(){
1169+
constiterator=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+
constisGeneratorComponent=
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+
(typeofReadableStream!== 'function' ||
1205+
!(resultinstanceofReadableStream))
1206+
){
1207+
constiterableChild=result;
1208+
constmultishot={
1209+
[ASYNC_ITERATOR]: function(){
1210+
constiterator=(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+
constisGeneratorComponent=
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+
returniterator;
1235+
},
1236+
};
1237+
if(__DEV__){
1238+
(multishot: any)._debugInfo=iterableChild._debugInfo;
1239+
}
1240+
return multishot;
1241+
}
1242+
returnresult;
1243+
}
1244+
11081245
functionrenderFunctionComponent<Props>(
11091246
request: Request,
11101247
task: Task,
@@ -1231,123 +1368,9 @@ function renderFunctionComponent<Props>(
12311368
throw null;
12321369
}
12331370

1234-
if(
1235-
typeofresult=== 'object' &&
1236-
result!==null&&
1237-
!isClientReference(result)
1238-
){
1239-
if(typeofresult.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-
constthenable: 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-
typeofresolvedValue==='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-
returnthenable.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-
constiteratorFn=getIteratorFn(result);
1271-
if(iteratorFn){
1272-
constiterableChild=result;
1273-
result={
1274-
[Symbol.iterator]: function(){
1275-
constiterator=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-
constisGeneratorComponent=
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-
}elseif(
1306-
enableFlightReadableStream&&
1307-
typeof(result: any)[ASYNC_ITERATOR]=== 'function' &&
1308-
(typeofReadableStream!== 'function' ||
1309-
!(resultinstanceofReadableStream))
1310-
){
1311-
constiterableChild=result;
1312-
result={
1313-
[ASYNC_ITERATOR]: function(){
1314-
constiterator=(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-
constisGeneratorComponent=
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-
returniterator;
1339-
},
1340-
};
1341-
if(__DEV__){
1342-
(result: any)._debugInfo=iterableChild._debugInfo;
1343-
}
1344-
}elseif(__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
constprevKeyPath=task.keyPath;
13531376
constprevImplicitSlot=task.implicitSlot;

0 commit comments

Comments
 (0)
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

Commit 3d2ab01

Browse files
authored
[Flight] Extract special cases for Server Component return value position (#31713)
This is just moving some code into a helper. We have a bunch of special cases for the return value slot of a Server Component that's different from just rendering that inside an object. This was getting a little tricky to reason about inline with the rest of rendering.
1 parent 76d603a commit 3d2ab01

1 file changed

Lines changed: 139 additions & 116 deletions

File tree

‎packages/react-server/src/ReactFlightServer.js‎

Lines changed: 139 additions & 116 deletions
Original file line numberDiff line numberDiff line change
@@ -1105,6 +1105,143 @@ function callWithDebugContextInDEV<A, T>(
11051105

11061106
constvoidHandler=()=>{};
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+
typeofresult!=='object'||
1118+
result===null||
1119+
isClientReference(result)
1120+
){
1121+
returnresult;
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+
constthenable: 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+
typeofresolvedValue==='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+
returnthenable.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+
constiteratorFn=getIteratorFn(result);
1165+
if(iteratorFn){
1166+
constiterableChild=result;
1167+
constmultiShot={
1168+
[Symbol.iterator]: function(){
1169+
constiterator=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+
constisGeneratorComponent=
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+
(typeofReadableStream!== 'function' ||
1205+
!(resultinstanceofReadableStream))
1206+
){
1207+
constiterableChild=result;
1208+
constmultishot={
1209+
[ASYNC_ITERATOR]: function(){
1210+
constiterator=(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+
constisGeneratorComponent=
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+
returniterator;
1235+
},
1236+
};
1237+
if(__DEV__){
1238+
(multishot: any)._debugInfo=iterableChild._debugInfo;
1239+
}
1240+
return multishot;
1241+
}
1242+
returnresult;
1243+
}
1244+
11081245
functionrenderFunctionComponent<Props>(
11091246
request: Request,
11101247
task: Task,
@@ -1231,123 +1368,9 @@ function renderFunctionComponent<Props>(
12311368
throw null;
12321369
}
12331370

1234-
if(
1235-
typeofresult=== 'object' &&
1236-
result!==null&&
1237-
!isClientReference(result)
1238-
){
1239-
if(typeofresult.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-
constthenable: 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-
typeofresolvedValue==='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-
returnthenable.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-
constiteratorFn=getIteratorFn(result);
1271-
if(iteratorFn){
1272-
constiterableChild=result;
1273-
result={
1274-
[Symbol.iterator]: function(){
1275-
constiterator=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-
constisGeneratorComponent=
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-
}elseif(
1306-
enableFlightReadableStream&&
1307-
typeof(result: any)[ASYNC_ITERATOR]=== 'function' &&
1308-
(typeofReadableStream!== 'function' ||
1309-
!(resultinstanceofReadableStream))
1310-
){
1311-
constiterableChild=result;
1312-
result={
1313-
[ASYNC_ITERATOR]: function(){
1314-
constiterator=(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-
constisGeneratorComponent=
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-
returniterator;
1339-
},
1340-
};
1341-
if(__DEV__){
1342-
(result: any)._debugInfo=iterableChild._debugInfo;
1343-
}
1344-
}elseif(__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
constprevKeyPath=task.keyPath;
13531376
constprevImplicitSlot=task.implicitSlot;

0 commit comments

Comments
 (0)
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Commit 3d2ab01

Browse files
authored
[Flight] Extract special cases for Server Component return value position (#31713)
This is just moving some code into a helper. We have a bunch of special cases for the return value slot of a Server Component that's different from just rendering that inside an object. This was getting a little tricky to reason about inline with the rest of rendering.
1 parent 76d603a commit 3d2ab01

1 file changed

Lines changed: 139 additions & 116 deletions

File tree

‎packages/react-server/src/ReactFlightServer.js‎

Lines changed: 139 additions & 116 deletions
Original file line numberDiff line numberDiff line change
@@ -1105,6 +1105,143 @@ function callWithDebugContextInDEV<A, T>(
11051105

11061106
constvoidHandler=()=>{};
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+
typeofresult!=='object'||
1118+
result===null||
1119+
isClientReference(result)
1120+
){
1121+
returnresult;
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+
constthenable: 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+
typeofresolvedValue==='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+
returnthenable.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+
constiteratorFn=getIteratorFn(result);
1165+
if(iteratorFn){
1166+
constiterableChild=result;
1167+
constmultiShot={
1168+
[Symbol.iterator]: function(){
1169+
constiterator=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+
constisGeneratorComponent=
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+
(typeofReadableStream!== 'function' ||
1205+
!(resultinstanceofReadableStream))
1206+
){
1207+
constiterableChild=result;
1208+
constmultishot={
1209+
[ASYNC_ITERATOR]: function(){
1210+
constiterator=(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+
constisGeneratorComponent=
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+
returniterator;
1235+
},
1236+
};
1237+
if(__DEV__){
1238+
(multishot: any)._debugInfo=iterableChild._debugInfo;
1239+
}
1240+
return multishot;
1241+
}
1242+
returnresult;
1243+
}
1244+
11081245
functionrenderFunctionComponent<Props>(
11091246
request: Request,
11101247
task: Task,
@@ -1231,123 +1368,9 @@ function renderFunctionComponent<Props>(
12311368
throw null;
12321369
}
12331370

1234-
if(
1235-
typeofresult=== 'object' &&
1236-
result!==null&&
1237-
!isClientReference(result)
1238-
){
1239-
if(typeofresult.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-
constthenable: 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-
typeofresolvedValue==='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-
returnthenable.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-
constiteratorFn=getIteratorFn(result);
1271-
if(iteratorFn){
1272-
constiterableChild=result;
1273-
result={
1274-
[Symbol.iterator]: function(){
1275-
constiterator=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-
constisGeneratorComponent=
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-
}elseif(
1306-
enableFlightReadableStream&&
1307-
typeof(result: any)[ASYNC_ITERATOR]=== 'function' &&
1308-
(typeofReadableStream!== 'function' ||
1309-
!(resultinstanceofReadableStream))
1310-
){
1311-
constiterableChild=result;
1312-
result={
1313-
[ASYNC_ITERATOR]: function(){
1314-
constiterator=(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-
constisGeneratorComponent=
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-
returniterator;
1339-
},
1340-
};
1341-
if(__DEV__){
1342-
(result: any)._debugInfo=iterableChild._debugInfo;
1343-
}
1344-
}elseif(__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
constprevKeyPath=task.keyPath;
13531376
constprevImplicitSlot=task.implicitSlot;

0 commit comments

Comments
 (0)
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Commit 3d2ab01

Browse files
authored
[Flight] Extract special cases for Server Component return value position (#31713)
This is just moving some code into a helper. We have a bunch of special cases for the return value slot of a Server Component that's different from just rendering that inside an object. This was getting a little tricky to reason about inline with the rest of rendering.
1 parent 76d603a commit 3d2ab01

1 file changed

Lines changed: 139 additions & 116 deletions

File tree

‎packages/react-server/src/ReactFlightServer.js‎

Lines changed: 139 additions & 116 deletions
Original file line numberDiff line numberDiff line change
@@ -1105,6 +1105,143 @@ function callWithDebugContextInDEV<A, T>(
11051105

11061106
constvoidHandler=()=>{};
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+
typeofresult!=='object'||
1118+
result===null||
1119+
isClientReference(result)
1120+
){
1121+
returnresult;
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+
constthenable: 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+
typeofresolvedValue==='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+
returnthenable.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+
constiteratorFn=getIteratorFn(result);
1165+
if(iteratorFn){
1166+
constiterableChild=result;
1167+
constmultiShot={
1168+
[Symbol.iterator]: function(){
1169+
constiterator=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+
constisGeneratorComponent=
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+
(typeofReadableStream!== 'function' ||
1205+
!(resultinstanceofReadableStream))
1206+
){
1207+
constiterableChild=result;
1208+
constmultishot={
1209+
[ASYNC_ITERATOR]: function(){
1210+
constiterator=(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+
constisGeneratorComponent=
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+
returniterator;
1235+
},
1236+
};
1237+
if(__DEV__){
1238+
(multishot: any)._debugInfo=iterableChild._debugInfo;
1239+
}
1240+
return multishot;
1241+
}
1242+
returnresult;
1243+
}
1244+
11081245
functionrenderFunctionComponent<Props>(
11091246
request: Request,
11101247
task: Task,
@@ -1231,123 +1368,9 @@ function renderFunctionComponent<Props>(
12311368
throw null;
12321369
}
12331370

1234-
if(
1235-
typeofresult=== 'object' &&
1236-
result!==null&&
1237-
!isClientReference(result)
1238-
){
1239-
if(typeofresult.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-
constthenable: 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-
typeofresolvedValue==='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-
returnthenable.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-
constiteratorFn=getIteratorFn(result);
1271-
if(iteratorFn){
1272-
constiterableChild=result;
1273-
result={
1274-
[Symbol.iterator]: function(){
1275-
constiterator=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-
constisGeneratorComponent=
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-
}elseif(
1306-
enableFlightReadableStream&&
1307-
typeof(result: any)[ASYNC_ITERATOR]=== 'function' &&
1308-
(typeofReadableStream!== 'function' ||
1309-
!(resultinstanceofReadableStream))
1310-
){
1311-
constiterableChild=result;
1312-
result={
1313-
[ASYNC_ITERATOR]: function(){
1314-
constiterator=(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-
constisGeneratorComponent=
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-
returniterator;
1339-
},
1340-
};
1341-
if(__DEV__){
1342-
(result: any)._debugInfo=iterableChild._debugInfo;
1343-
}
1344-
}elseif(__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
constprevKeyPath=task.keyPath;
13531376
constprevImplicitSlot=task.implicitSlot;

0 commit comments

Comments
 (0)
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

Commit 3d2ab01

Browse files
authored
[Flight] Extract special cases for Server Component return value position (#31713)
This is just moving some code into a helper. We have a bunch of special cases for the return value slot of a Server Component that's different from just rendering that inside an object. This was getting a little tricky to reason about inline with the rest of rendering.
1 parent 76d603a commit 3d2ab01

1 file changed

Lines changed: 139 additions & 116 deletions

File tree

‎packages/react-server/src/ReactFlightServer.js‎

Lines changed: 139 additions & 116 deletions
Original file line numberDiff line numberDiff line change
@@ -1105,6 +1105,143 @@ function callWithDebugContextInDEV<A, T>(
11051105

11061106
constvoidHandler=()=>{};
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+
typeofresult!=='object'||
1118+
result===null||
1119+
isClientReference(result)
1120+
){
1121+
returnresult;
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+
constthenable: 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+
typeofresolvedValue==='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+
returnthenable.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+
constiteratorFn=getIteratorFn(result);
1165+
if(iteratorFn){
1166+
constiterableChild=result;
1167+
constmultiShot={
1168+
[Symbol.iterator]: function(){
1169+
constiterator=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+
constisGeneratorComponent=
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+
(typeofReadableStream!== 'function' ||
1205+
!(resultinstanceofReadableStream))
1206+
){
1207+
constiterableChild=result;
1208+
constmultishot={
1209+
[ASYNC_ITERATOR]: function(){
1210+
constiterator=(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+
constisGeneratorComponent=
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+
returniterator;
1235+
},
1236+
};
1237+
if(__DEV__){
1238+
(multishot: any)._debugInfo=iterableChild._debugInfo;
1239+
}
1240+
return multishot;
1241+
}
1242+
returnresult;
1243+
}
1244+
11081245
functionrenderFunctionComponent<Props>(
11091246
request: Request,
11101247
task: Task,
@@ -1231,123 +1368,9 @@ function renderFunctionComponent<Props>(
12311368
throw null;
12321369
}
12331370

1234-
if(
1235-
typeofresult=== 'object' &&
1236-
result!==null&&
1237-
!isClientReference(result)
1238-
){
1239-
if(typeofresult.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-
constthenable: 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-
typeofresolvedValue==='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-
returnthenable.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-
constiteratorFn=getIteratorFn(result);
1271-
if(iteratorFn){
1272-
constiterableChild=result;
1273-
result={
1274-
[Symbol.iterator]: function(){
1275-
constiterator=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-
constisGeneratorComponent=
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-
}elseif(
1306-
enableFlightReadableStream&&
1307-
typeof(result: any)[ASYNC_ITERATOR]=== 'function' &&
1308-
(typeofReadableStream!== 'function' ||
1309-
!(resultinstanceofReadableStream))
1310-
){
1311-
constiterableChild=result;
1312-
result={
1313-
[ASYNC_ITERATOR]: function(){
1314-
constiterator=(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-
constisGeneratorComponent=
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-
returniterator;
1339-
},
1340-
};
1341-
if(__DEV__){
1342-
(result: any)._debugInfo=iterableChild._debugInfo;
1343-
}
1344-
}elseif(__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
constprevKeyPath=task.keyPath;
13531376
constprevImplicitSlot=task.implicitSlot;

0 commit comments

Comments
 (0)