Skip to content

Commit

Permalink
Allow functions to be used as module references (#25137)
Browse files Browse the repository at this point in the history
  • Loading branch information
sebmarkbage authored Aug 25, 2022
1 parent 38c5d8a commit d0f3966
Showing 1 changed file with 54 additions and 39 deletions.
93 changes: 54 additions & 39 deletions packages/react-server/src/ReactFlightServer.js
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,10 @@ function attemptResolveElement(
);
}
if (typeof type === 'function') {
if (isModuleReference(type)) {
// This is a reference to a client component.
return [REACT_ELEMENT_TYPE, type, key, props];
}
// This is a server-side component.
return type(props);
} else if (typeof type === 'string') {
Expand Down Expand Up @@ -295,6 +299,52 @@ function serializeByRefID(id: number): string {
return '@' + id.toString(16);
}

function serializeModuleReference(
request: Request,
parent: {+[key: string | number]: ReactModel} | $ReadOnlyArray<ReactModel>,
key: string,
moduleReference: ModuleReference<any>,
): string {
const moduleKey: ModuleKey = getModuleKey(moduleReference);
const writtenModules = request.writtenModules;
const existingId = writtenModules.get(moduleKey);
if (existingId !== undefined) {
if (parent[0] === REACT_ELEMENT_TYPE && key === '1') {
// If we're encoding the "type" of an element, we can refer
// to that by a lazy reference instead of directly since React
// knows how to deal with lazy values. This lets us suspend
// on this component rather than its parent until the code has
// loaded.
return serializeByRefID(existingId);
}
return serializeByValueID(existingId);
}
try {
const moduleMetaData: ModuleMetaData = resolveModuleMetaData(
request.bundlerConfig,
moduleReference,
);
request.pendingChunks++;
const moduleId = request.nextChunkId++;
emitModuleChunk(request, moduleId, moduleMetaData);
writtenModules.set(moduleKey, moduleId);
if (parent[0] === REACT_ELEMENT_TYPE && key === '1') {
// If we're encoding the "type" of an element, we can refer
// to that by a lazy reference instead of directly since React
// knows how to deal with lazy values. This lets us suspend
// on this component rather than its parent until the code has
// loaded.
return serializeByRefID(moduleId);
}
return serializeByValueID(moduleId);
} catch (x) {
request.pendingChunks++;
const errorId = request.nextChunkId++;
emitErrorChunk(request, errorId, x);
return serializeByValueID(errorId);
}
}

function escapeStringValue(value: string): string {
if (value[0] === '$' || value[0] === '@') {
// We need to escape $ or @ prefixed strings since we use those to encode
Expand Down Expand Up @@ -561,45 +611,7 @@ export function resolveModelToJSON(

if (typeof value === 'object') {
if (isModuleReference(value)) {
const moduleReference: ModuleReference<any> = (value: any);
const moduleKey: ModuleKey = getModuleKey(moduleReference);
const writtenModules = request.writtenModules;
const existingId = writtenModules.get(moduleKey);
if (existingId !== undefined) {
if (parent[0] === REACT_ELEMENT_TYPE && key === '1') {
// If we're encoding the "type" of an element, we can refer
// to that by a lazy reference instead of directly since React
// knows how to deal with lazy values. This lets us suspend
// on this component rather than its parent until the code has
// loaded.
return serializeByRefID(existingId);
}
return serializeByValueID(existingId);
}
try {
const moduleMetaData: ModuleMetaData = resolveModuleMetaData(
request.bundlerConfig,
moduleReference,
);
request.pendingChunks++;
const moduleId = request.nextChunkId++;
emitModuleChunk(request, moduleId, moduleMetaData);
writtenModules.set(moduleKey, moduleId);
if (parent[0] === REACT_ELEMENT_TYPE && key === '1') {
// If we're encoding the "type" of an element, we can refer
// to that by a lazy reference instead of directly since React
// knows how to deal with lazy values. This lets us suspend
// on this component rather than its parent until the code has
// loaded.
return serializeByRefID(moduleId);
}
return serializeByValueID(moduleId);
} catch (x) {
request.pendingChunks++;
const errorId = request.nextChunkId++;
emitErrorChunk(request, errorId, x);
return serializeByValueID(errorId);
}
return serializeModuleReference(request, parent, key, (value: any));
} else if ((value: any).$$typeof === REACT_PROVIDER_TYPE) {
const providerKey = ((value: any): ReactProviderType<any>)._context
._globalName;
Expand Down Expand Up @@ -673,6 +685,9 @@ export function resolveModelToJSON(
}

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

0 comments on commit d0f3966

Please sign in to comment.