[RSC @ Meta] Simplify implementation of isClientReference, getClientReferenceKey, resolveClientReferenceMetadata (#27839)
For clientReferences we can just check the instance of the `clientReference`. The implementation of `isClientReference` is provided via configuration. The class for ClientReference has to implement an interface that has `getModuleId() method.
Andrey Lunyov committed
Dec 19, 2023 at 09:17 UTC
cb2439624f43c510007f65aea5c50a8bb97917e4
3 files changed
+42
-40
packages/react-server-dom-fb/src/ReactFlightDOMServerFB.js
+6
-3
@@ -13,7 +13,8 @@ import type {
13
Chunk,
14
PrecomputedChunk,
15
} from 'react-server/src/ReactServerStreamConfig';
16
-import type {ClientManifest} from './ReactFlightReferencesFB';
16
+
17
+import {setCheckIsClientReference} from './ReactFlightReferencesFB';
18
19
import {
20
createRequest,
@@ -28,6 +29,7 @@ export {
29
registerServerReference,
30
getRequestedClientReferencesKeys,
31
clearRequestedClientReferencesKeysSet,
32
+ setCheckIsClientReference,
33
} from './ReactFlightReferencesFB';
34
35
type Options = {
@@ -37,7 +39,6 @@ type Options = {
39
function renderToDestination(
40
destination: Destination,
41
model: ReactClientValue,
40
- bundlerConfig: ClientManifest,
42
options?: Options,
43
): void {
44
if (!configured) {
@@ -47,7 +48,7 @@ function renderToDestination(
48
}
49
const request = createRequest(
50
model,
50
- bundlerConfig,
51
+ null,
52
options ? options.onError : undefined,
53
);
54
startWork(request);
@@ -56,12 +57,14 @@ function renderToDestination(
57
58
type Config = {
59
byteLength: (chunk: Chunk | PrecomputedChunk) => number,
60
+ isClientReference: (reference: mixed) => boolean,
61
};
62
63
let configured = false;
64
65
function setConfig(config: Config): void {
66
setByteLengthOfChunkImplementation(config.byteLength);
67
+ setCheckIsClientReference(config.isClientReference);
68
configured = true;
69
}
70
packages/react-server-dom-fb/src/ReactFlightReferencesFB.js
+24
-32
@@ -7,15 +7,16 @@
7
* @flow
8
*/
9
10
-export opaque type ClientManifest = mixed;
10
+export type ClientManifest = null;
11
12
// eslint-disable-next-line no-unused-vars
13
export type ServerReference<T> = string;
14
15
// eslint-disable-next-line no-unused-vars
16
-export type ClientReference<T> = string;
16
+export type ClientReference<T> = {
17
+ getModuleId(): ClientReferenceKey,
18
+};
19
18
-const registeredClientReferences = new Map<mixed, ClientReferenceMetadata>();
20
const requestedClientReferencesKeys = new Set<ClientReferenceKey>();
21
22
export type ClientReferenceKey = string;
@@ -26,54 +27,45 @@ export type ClientReferenceMetadata = {
27
28
export type ServerReferenceId = string;
29
30
+let checkIsClientReference: (clientReference: mixed) => boolean;
31
+
32
+export function setCheckIsClientReference(
33
+ impl: (clientReference: mixed) => boolean,
34
+): void {
35
+ checkIsClientReference = impl;
36
+}
37
+
38
export function registerClientReference<T>(
39
clientReference: ClientReference<T>,
31
- moduleId: ClientReferenceKey,
32
-): ClientReference<T> {
33
- const exportName = 'default'; // Currently, we only support modules with `default` export
34
- registeredClientReferences.set(clientReference, {
35
- moduleId,
36
- exportName,
37
- });
38
-
39
- return clientReference;
40
-}
40
+): void {}
41
42
-export function isClientReference<T>(reference: T): boolean {
43
- return registeredClientReferences.has(reference);
42
+export function isClientReference(reference: mixed): boolean {
43
+ if (checkIsClientReference == null) {
44
+ throw new Error('Expected implementation for checkIsClientReference.');
45
+ }
46
+ return checkIsClientReference(reference);
47
}
48
49
export function getClientReferenceKey<T>(
50
clientReference: ClientReference<T>,
51
): ClientReferenceKey {
49
- const reference = registeredClientReferences.get(clientReference);
50
- if (reference != null) {
51
- requestedClientReferencesKeys.add(reference.moduleId);
52
- return reference.moduleId;
53
- }
52
+ const moduleId = clientReference.getModuleId();
53
+ requestedClientReferencesKeys.add(moduleId);
54
55
- throw new Error(
56
- 'Expected client reference ' + clientReference + ' to be registered.',
57
- );
55
+ return clientReference.getModuleId();
56
}
57
58
export function resolveClientReferenceMetadata<T>(
59
config: ClientManifest,
60
clientReference: ClientReference<T>,
61
): ClientReferenceMetadata {
64
- const metadata = registeredClientReferences.get(clientReference);
65
- if (metadata != null) {
66
- return metadata;
67
- }
68
-
69
- throw new Error(
70
- 'Expected client reference ' + clientReference + ' to be registered.',
71
- );
62
+ return {moduleId: clientReference.getModuleId(), exportName: 'default'};
63
}
64
65
export function registerServerReference<T>(
66
serverReference: ServerReference<T>,
76
- exportName: string,
67
+ id: string,
68
+ exportName: null | string,
69
): ServerReference<T> {
70
throw new Error('registerServerReference: Not Implemented.');
71
}
packages/react-server-dom-fb/src/__tests__/ReactFlightDOMServerFB-test.internal.js
+12
-5
@@ -28,7 +28,6 @@ let ReactDOMClient;
28
let ReactServerDOMServer;
29
let ReactServerDOMClient;
30
let Suspense;
31
-let registerClientReference;
31
32
class Destination {
33
#buffer = '';
@@ -57,14 +56,22 @@ class Destination {
56
onError() {}
57
}
58
59
+class ClientReferenceImpl {
60
+ constructor(moduleId) {
61
+ this.moduleId = moduleId;
62
+ }
63
+
64
+ getModuleId() {
65
+ return this.moduleId;
66
+ }
67
+}
68
+
69
describe('ReactFlightDOM for FB', () => {
70
beforeEach(() => {
71
// For this first reset we are going to load the dom-node version of react-server-dom-turbopack/server
72
// This can be thought of as essentially being the React Server Components scope with react-server
73
// condition
74
jest.resetModules();
66
- registerClientReference =
67
- require('../ReactFlightReferencesFB').registerClientReference;
75
76
jest.mock('react', () => require('react/src/ReactSharedSubsetFB'));
77
@@ -78,8 +85,7 @@ describe('ReactFlightDOM for FB', () => {
85
});
86
87
clientExports = value => {
81
- registerClientReference(value, value.name);
82
- return value;
88
+ return new ClientReferenceImpl(value.name);
89
};
90
91
moduleMap = {
@@ -91,6 +97,7 @@ describe('ReactFlightDOM for FB', () => {
97
ReactServerDOMServer = require('../ReactFlightDOMServerFB');
98
ReactServerDOMServer.setConfig({
99
byteLength: str => Buffer.byteLength(str),
100
+ isClientReference: reference => reference instanceof ClientReferenceImpl,
101
});
102
103
// This reset is to load modules for the SSR/Browser scope.