Skip to content

Commit d0f3966

Browse files
authored
Allow functions to be used as module references (#25137)
1 parent 38c5d8a commit d0f3966

File tree

1 file changed

+54
-39
lines changed

1 file changed

+54
-39
lines changed

packages/react-server/src/ReactFlightServer.js

+54-39
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,10 @@ function attemptResolveElement(
195195
);
196196
}
197197
if (typeof type === 'function') {
198+
if (isModuleReference(type)) {
199+
// This is a reference to a client component.
200+
return [REACT_ELEMENT_TYPE, type, key, props];
201+
}
198202
// This is a server-side component.
199203
return type(props);
200204
} else if (typeof type === 'string') {
@@ -295,6 +299,52 @@ function serializeByRefID(id: number): string {
295299
return '@' + id.toString(16);
296300
}
297301

302+
function serializeModuleReference(
303+
request: Request,
304+
parent: {+[key: string | number]: ReactModel} | $ReadOnlyArray<ReactModel>,
305+
key: string,
306+
moduleReference: ModuleReference<any>,
307+
): string {
308+
const moduleKey: ModuleKey = getModuleKey(moduleReference);
309+
const writtenModules = request.writtenModules;
310+
const existingId = writtenModules.get(moduleKey);
311+
if (existingId !== undefined) {
312+
if (parent[0] === REACT_ELEMENT_TYPE && key === '1') {
313+
// If we're encoding the "type" of an element, we can refer
314+
// to that by a lazy reference instead of directly since React
315+
// knows how to deal with lazy values. This lets us suspend
316+
// on this component rather than its parent until the code has
317+
// loaded.
318+
return serializeByRefID(existingId);
319+
}
320+
return serializeByValueID(existingId);
321+
}
322+
try {
323+
const moduleMetaData: ModuleMetaData = resolveModuleMetaData(
324+
request.bundlerConfig,
325+
moduleReference,
326+
);
327+
request.pendingChunks++;
328+
const moduleId = request.nextChunkId++;
329+
emitModuleChunk(request, moduleId, moduleMetaData);
330+
writtenModules.set(moduleKey, moduleId);
331+
if (parent[0] === REACT_ELEMENT_TYPE && key === '1') {
332+
// If we're encoding the "type" of an element, we can refer
333+
// to that by a lazy reference instead of directly since React
334+
// knows how to deal with lazy values. This lets us suspend
335+
// on this component rather than its parent until the code has
336+
// loaded.
337+
return serializeByRefID(moduleId);
338+
}
339+
return serializeByValueID(moduleId);
340+
} catch (x) {
341+
request.pendingChunks++;
342+
const errorId = request.nextChunkId++;
343+
emitErrorChunk(request, errorId, x);
344+
return serializeByValueID(errorId);
345+
}
346+
}
347+
298348
function escapeStringValue(value: string): string {
299349
if (value[0] === '$' || value[0] === '@') {
300350
// We need to escape $ or @ prefixed strings since we use those to encode
@@ -561,45 +611,7 @@ export function resolveModelToJSON(
561611

562612
if (typeof value === 'object') {
563613
if (isModuleReference(value)) {
564-
const moduleReference: ModuleReference<any> = (value: any);
565-
const moduleKey: ModuleKey = getModuleKey(moduleReference);
566-
const writtenModules = request.writtenModules;
567-
const existingId = writtenModules.get(moduleKey);
568-
if (existingId !== undefined) {
569-
if (parent[0] === REACT_ELEMENT_TYPE && key === '1') {
570-
// If we're encoding the "type" of an element, we can refer
571-
// to that by a lazy reference instead of directly since React
572-
// knows how to deal with lazy values. This lets us suspend
573-
// on this component rather than its parent until the code has
574-
// loaded.
575-
return serializeByRefID(existingId);
576-
}
577-
return serializeByValueID(existingId);
578-
}
579-
try {
580-
const moduleMetaData: ModuleMetaData = resolveModuleMetaData(
581-
request.bundlerConfig,
582-
moduleReference,
583-
);
584-
request.pendingChunks++;
585-
const moduleId = request.nextChunkId++;
586-
emitModuleChunk(request, moduleId, moduleMetaData);
587-
writtenModules.set(moduleKey, moduleId);
588-
if (parent[0] === REACT_ELEMENT_TYPE && key === '1') {
589-
// If we're encoding the "type" of an element, we can refer
590-
// to that by a lazy reference instead of directly since React
591-
// knows how to deal with lazy values. This lets us suspend
592-
// on this component rather than its parent until the code has
593-
// loaded.
594-
return serializeByRefID(moduleId);
595-
}
596-
return serializeByValueID(moduleId);
597-
} catch (x) {
598-
request.pendingChunks++;
599-
const errorId = request.nextChunkId++;
600-
emitErrorChunk(request, errorId, x);
601-
return serializeByValueID(errorId);
602-
}
614+
return serializeModuleReference(request, parent, key, (value: any));
603615
} else if ((value: any).$$typeof === REACT_PROVIDER_TYPE) {
604616
const providerKey = ((value: any): ReactProviderType<any>)._context
605617
._globalName;
@@ -673,6 +685,9 @@ export function resolveModelToJSON(
673685
}
674686

675687
if (typeof value === 'function') {
688+
if (isModuleReference(value)) {
689+
return serializeModuleReference(request, parent, key, (value: any));
690+
}
676691
if (/^on[A-Z]/.test(key)) {
677692
throw new Error(
678693
'Event handlers cannot be passed to client component props. ' +

0 commit comments

Comments
 (0)