mirror of
https://github.com/zebrajr/react.git
synced 2026-01-15 12:15:22 +00:00
Enable passing Server References from Server to Client (#26124)
This is the first of a series of PRs, that let you pass functions, by reference, to the client and back. E.g. through Server Context. It's like client references but they're opaque on the client and resolved on the server. To do this, for security, you must opt-in to exposing these functions to the client using the `"use server"` directive. The `"use client"` directive lets you enter the client from the server. The `"use server"` directive lets you enter the server from the client. This works by tagging those functions as Server References. We could potentially expand this to other non-serializable or stateful objects too like classes. This only implements server->server CJS imports and server->server ESM imports. We really should add a loader to the webpack plug-in for client->server imports too. I'll leave closures as an exercise for integrators. You can't "call" a client reference on the server, however, you can "call" a server reference on the client. This invokes a callback on the Flight client options called `callServer`. This lets a router implement calling back to the server. Effectively creating an RPC. This is using JSON for serializing those arguments but more utils coming from client->server serialization.
This commit is contained in:
committed by
GitHub
parent
6c75d4e009
commit
ef9f6e77b8
@@ -361,7 +361,7 @@
|
||||
"372": "Cannot call unstable_createEventHandle with \"%s\", as it is not an event known to React.",
|
||||
"373": "This Hook is not supported in Server Components.",
|
||||
"374": "Event handlers cannot be passed to Client Component props.%s\nIf you need interactivity, consider converting part of this to a Client Component.",
|
||||
"375": "Functions cannot be passed directly to Client Components because they're not serializable.%s",
|
||||
"375": "Functions cannot be passed directly to Client Components unless you explicitly expose it by marking it with \"use server\".%s",
|
||||
"376": "Only global symbols received from Symbol.for(...) can be passed to Client Components. The symbol Symbol.for(%s) cannot be found among global symbols.%s",
|
||||
"377": "BigInt (%s) is not yet supported in Client Component props.%s",
|
||||
"378": "Type %s is not supported in Client Component props.%s",
|
||||
@@ -450,5 +450,6 @@
|
||||
"462": "Unexpected SuspendedReason. This is a bug in React.",
|
||||
"463": "ReactDOMServer.renderToNodeStream(): The Node Stream API is not available in Bun. Use ReactDOMServer.renderToReadableStream() instead.",
|
||||
"464": "ReactDOMServer.renderToStaticNodeStream(): The Node Stream API is not available in Bun. Use ReactDOMServer.renderToReadableStream() instead.",
|
||||
"465": "enableFizzExternalRuntime without enableFloat is not supported. This should never appear in production, since it means you are using a misconfigured React bundle."
|
||||
}
|
||||
"465": "enableFizzExternalRuntime without enableFloat is not supported. This should never appear in production, since it means you are using a misconfigured React bundle.",
|
||||
"466": "Trying to call a function from \"use server\" but the callServer option was not implemented in your router runtime."
|
||||
}
|
||||
20
scripts/flow/react-relay-hooks.js
vendored
20
scripts/flow/react-relay-hooks.js
vendored
@@ -46,19 +46,19 @@ declare module 'ReactFlightDOMRelayServerIntegration' {
|
||||
): void;
|
||||
declare export function close(destination: Destination): void;
|
||||
|
||||
declare export type ModuleMetaData = JSONValue;
|
||||
declare export function resolveModuleMetaData<T>(
|
||||
declare export type ClientReferenceMetadata = JSONValue;
|
||||
declare export function resolveClientReferenceMetadata<T>(
|
||||
config: BundlerConfig,
|
||||
resourceReference: JSResourceReference<T>,
|
||||
): ModuleMetaData;
|
||||
): ClientReferenceMetadata;
|
||||
}
|
||||
|
||||
declare module 'ReactFlightDOMRelayClientIntegration' {
|
||||
import type {JSResourceReference} from 'JSResourceReference';
|
||||
|
||||
declare export opaque type ModuleMetaData;
|
||||
declare export opaque type ClientReferenceMetadata;
|
||||
declare export function resolveClientReference<T>(
|
||||
moduleData: ModuleMetaData,
|
||||
moduleData: ClientReferenceMetadata,
|
||||
): JSResourceReference<T>;
|
||||
declare export function preloadModule<T>(
|
||||
moduleReference: JSResourceReference<T>,
|
||||
@@ -79,19 +79,19 @@ declare module 'ReactFlightNativeRelayServerIntegration' {
|
||||
): void;
|
||||
declare export function close(destination: Destination): void;
|
||||
|
||||
declare export type ModuleMetaData = JSONValue;
|
||||
declare export function resolveModuleMetaData<T>(
|
||||
declare export type ClientReferenceMetadata = JSONValue;
|
||||
declare export function resolveClientReferenceMetadata<T>(
|
||||
config: BundlerConfig,
|
||||
resourceReference: JSResourceReference<T>,
|
||||
): ModuleMetaData;
|
||||
): ClientReferenceMetadata;
|
||||
}
|
||||
|
||||
declare module 'ReactFlightNativeRelayClientIntegration' {
|
||||
import type {JSResourceReference} from 'JSResourceReference';
|
||||
|
||||
declare export opaque type ModuleMetaData;
|
||||
declare export opaque type ClientReferenceMetadata;
|
||||
declare export function resolveClientReference<T>(
|
||||
moduleData: ModuleMetaData,
|
||||
moduleData: ClientReferenceMetadata,
|
||||
): JSResourceReference<T>;
|
||||
declare export function preloadModule<T>(
|
||||
moduleReference: JSResourceReference<T>,
|
||||
|
||||
@@ -83,8 +83,9 @@ jest.mock('react-server/flight', () => {
|
||||
jest.mock(shimServerFormatConfigPath, () => config);
|
||||
jest.mock('react-server/src/ReactFlightServerBundlerConfigCustom', () => ({
|
||||
isClientReference: config.isClientReference,
|
||||
isServerReference: config.isServerReference,
|
||||
getClientReferenceKey: config.getClientReferenceKey,
|
||||
resolveModuleMetaData: config.resolveModuleMetaData,
|
||||
resolveClientReferenceMetadata: config.resolveClientReferenceMetadata,
|
||||
}));
|
||||
jest.mock(shimFlightServerConfigPath, () =>
|
||||
jest.requireActual(
|
||||
|
||||
@@ -225,6 +225,7 @@ function isProductionBundleType(bundleType) {
|
||||
switch (bundleType) {
|
||||
case NODE_ES2015:
|
||||
case NODE_ESM:
|
||||
return true;
|
||||
case UMD_DEV:
|
||||
case NODE_DEV:
|
||||
case BUN_DEV:
|
||||
@@ -377,12 +378,18 @@ function getPlugins(
|
||||
// Please don't enable this for anything else!
|
||||
isUMDBundle && entry === 'react-art' && commonjs(),
|
||||
// Apply dead code elimination and/or minification.
|
||||
// closure doesn't yet support leaving ESM imports intact
|
||||
isProduction &&
|
||||
bundleType !== NODE_ESM &&
|
||||
closure({
|
||||
compilation_level: 'SIMPLE',
|
||||
language_in: 'ECMASCRIPT_2015',
|
||||
language_in: 'ECMASCRIPT_2018',
|
||||
language_out:
|
||||
bundleType === BROWSER_SCRIPT ? 'ECMASCRIPT5' : 'ECMASCRIPT5_STRICT',
|
||||
bundleType === NODE_ES2015
|
||||
? 'ECMASCRIPT_2018'
|
||||
: bundleType === BROWSER_SCRIPT
|
||||
? 'ECMASCRIPT5'
|
||||
: 'ECMASCRIPT5_STRICT',
|
||||
env: 'CUSTOM',
|
||||
warning_level: 'QUIET',
|
||||
apply_input_source_maps: false,
|
||||
|
||||
@@ -417,7 +417,7 @@ const bundles = [
|
||||
bundleTypes: [FB_WWW_DEV, FB_WWW_PROD],
|
||||
moduleType: RENDERER,
|
||||
entry: 'react-server-dom-relay/server',
|
||||
global: 'ReactFlightDOMRelayServer', // TODO: Rename to Writer
|
||||
global: 'ReactFlightDOMRelayServer',
|
||||
minifyWithProdErrorCodes: false,
|
||||
wrapWithModuleBoundaries: false,
|
||||
externals: [
|
||||
@@ -465,7 +465,7 @@ const bundles = [
|
||||
bundleTypes: [RN_FB_DEV, RN_FB_PROD],
|
||||
moduleType: RENDERER,
|
||||
entry: 'react-server-native-relay',
|
||||
global: 'ReactFlightNativeRelayClient', // TODO: Rename to Reader
|
||||
global: 'ReactFlightNativeRelayClient',
|
||||
minifyWithProdErrorCodes: true,
|
||||
wrapWithModuleBoundaries: false,
|
||||
externals: [
|
||||
|
||||
Reference in New Issue
Block a user