11import * as React from 'react' ;
22import { createFromReadableStream } from 'react-on-rails-rsc/client.browser' ;
3- import { fetch , wrapInNewPromise } from './utils.ts' ;
3+ import { createRSCPayloadKey , fetch , wrapInNewPromise } from './utils.ts' ;
44import transformRSCStreamAndReplayConsoleLogs from './transformRSCStreamAndReplayConsoleLogs.ts' ;
55import { RailsContext } from './types/index.ts' ;
66
@@ -10,12 +10,10 @@ declare global {
1010 }
1111}
1212
13- type ClientGetReactServerComponentProps = {
13+ export type ClientGetReactServerComponentProps = {
1414 componentName : string ;
1515 componentProps : unknown ;
16- railsContext : RailsContext ;
1716 enforceRefetch ?: boolean ;
18- createRSCPayloadKey : ( componentName : string , componentProps : unknown ) => string ;
1917} ;
2018
2119const createFromFetch = async ( fetchPromise : Promise < Response > ) => {
@@ -40,11 +38,16 @@ const createFromFetch = async (fetchPromise: Promise<Response>) => {
4038 * This is used for client-side navigation or when rendering components
4139 * that weren't part of the initial server render.
4240 *
43- * @param props - Object containing component name, props, and railsContext
41+ * @param props - Object containing component name and props
42+ * @param railsContext - The Rails context containing configuration
4443 * @returns A Promise resolving to the rendered React element
4544 * @throws Error if RSC payload generation URL path is not configured or network request fails
4645 */
47- const fetchRSC = ( { componentName, componentProps, railsContext } : ClientGetReactServerComponentProps ) => {
46+ const fetchRSC = ( {
47+ componentName,
48+ componentProps,
49+ railsContext,
50+ } : ClientGetReactServerComponentProps & { railsContext : RailsContext } ) => {
4851 const { rscPayloadGenerationUrlPath } = railsContext ;
4952
5053 if ( ! rscPayloadGenerationUrlPath ) {
@@ -131,41 +134,48 @@ const createFromPreloadedPayloads = (payloads: string[]) => {
131134} ;
132135
133136/**
134- * Fetches and renders a server component on the client side.
137+ * Creates a function that fetches and renders a server component on the client side.
135138 *
136- * This function:
137- * 1. Checks for embedded RSC payloads in window.REACT_ON_RAILS_RSC_PAYLOADS
139+ * This style of higher-order function is necessary as the function that gets server components
140+ * on server has different parameters than the function that gets them on client. The environment
141+ * dependent parameters (domNodeId, railsContext) are passed from the `wrapServerComponentRenderer`
142+ * function, while the environment agnostic parameters (componentName, componentProps, enforceRefetch)
143+ * are passed from the RSCProvider which is environment agnostic.
144+ *
145+ * The returned function:
146+ * 1. Checks for embedded RSC payloads in window.REACT_ON_RAILS_RSC_PAYLOADS using the domNodeId
138147 * 2. If found, uses the embedded payload to avoid an HTTP request
139148 * 3. If not found (during client navigation or dynamic rendering), fetches via HTTP
140149 * 4. Processes the RSC payload into React elements
141150 *
142151 * The embedded payload approach ensures optimal performance during initial page load,
143152 * while the HTTP fallback enables dynamic rendering after navigation.
144153 *
154+ * @param domNodeId - The DOM node ID to create a unique key for the RSC payload store
155+ * @param railsContext - Context for the current request, shared across all components
156+ * @returns A function that accepts RSC parameters and returns a Promise resolving to the rendered React element
157+ *
158+ * The returned function accepts:
145159 * @param componentName - Name of the server component to render
146160 * @param componentProps - Props to pass to the server component
147- * @param railsContext - Context for the current request
148161 * @param enforceRefetch - Whether to enforce a refetch of the component
149- * @returns A Promise resolving to the rendered React element
150162 *
151163 * @important This is an internal function. End users should not use this directly.
152164 * Instead, use the useRSC hook which provides getComponent and refetchComponent functions
153165 * for fetching or retrieving cached server components. For rendering server components,
154166 * consider using RSCRoute component which handles the rendering logic automatically.
155167 */
156- const getReactServerComponent = ( {
157- componentName,
158- componentProps,
159- railsContext,
160- enforceRefetch = false ,
161- createRSCPayloadKey,
162- } : ClientGetReactServerComponentProps ) => {
163- const componentKey = createRSCPayloadKey ( componentName , componentProps ) ;
164- const payloads = window . REACT_ON_RAILS_RSC_PAYLOADS ?. [ componentKey ] ;
165- if ( ! enforceRefetch && payloads ) {
166- return createFromPreloadedPayloads ( payloads ) ;
167- }
168- return fetchRSC ( { componentName, componentProps, railsContext, createRSCPayloadKey } ) ;
169- } ;
168+ const getReactServerComponent =
169+ ( domNodeId : string , railsContext : RailsContext ) =>
170+ ( { componentName, componentProps, enforceRefetch = false } : ClientGetReactServerComponentProps ) => {
171+ const componentCacheKey = createRSCPayloadKey ( componentName , componentProps ) ;
172+
173+ const rscPayloadKey = `${ componentCacheKey } -${ domNodeId } ` ;
174+ const payloads = window . REACT_ON_RAILS_RSC_PAYLOADS ?. [ rscPayloadKey ] ;
175+ if ( ! enforceRefetch && payloads ) {
176+ return createFromPreloadedPayloads ( payloads ) ;
177+ }
178+ return fetchRSC ( { componentName, componentProps, railsContext } ) ;
179+ } ;
170180
171181export default getReactServerComponent ;
0 commit comments