@samitouri / QOS-React-2 / commits / cb2439624f

[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.