Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Flight] Allow functions to be used as module references #25137

Merged
merged 1 commit into from
Aug 25, 2022
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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