UNPKG

react-on-rails

Version:

react-on-rails JavaScript for react_on_rails Ruby gem

370 lines 14.8 kB
import type { ReactElement, ReactNode, Component, ComponentType } from 'react'; import type { PipeableStream } from 'react-dom/server'; import type { Readable } from 'stream'; /** * Don't import Redux just for the type definitions * See https://github.com/shakacode/react_on_rails/issues/1321 * and https://redux.js.org/api/store for the actual API. * @see {import('redux').Store} */ type Store = { getState(): unknown; }; type ReactComponent = ComponentType<any> | string; export type RailsContext = { componentRegistryTimeout: number; railsEnv: string; inMailer: boolean; i18nLocale: string; i18nDefaultLocale: string; rorVersion: string; rorPro: boolean; rorProVersion?: string; href: string; location: string; scheme: string; host: string; port: number | null; pathname: string; search: string | null; httpAcceptLanguage: string; rscPayloadGenerationUrlPath?: string; } & ({ serverSide: false; } | { serverSide: true; serverSideRSCPayloadParameters?: unknown; reactClientManifestFileName?: string; reactServerClientManifestFileName?: string; getRSCPayloadStream: (componentName: string, props: unknown) => Promise<NodeJS.ReadableStream>; }); export type RailsContextWithServerComponentMetadata = RailsContext & { serverSide: true; serverSideRSCPayloadParameters?: unknown; reactClientManifestFileName: string; reactServerClientManifestFileName: string; }; export type RailsContextWithServerStreamingCapabilities = RailsContextWithServerComponentMetadata & { getRSCPayloadStream: (componentName: string, props: unknown) => Promise<NodeJS.ReadableStream>; addPostSSRHook: (hook: () => void) => void; }; export declare const assertRailsContextWithServerComponentMetadata: (context: RailsContext | undefined) => asserts context is RailsContextWithServerComponentMetadata; export declare const assertRailsContextWithServerStreamingCapabilities: (context: RailsContext | undefined) => asserts context is RailsContextWithServerStreamingCapabilities; type AuthenticityHeaders = Record<string, string> & { 'X-CSRF-Token': string | null; 'X-Requested-With': string; }; type StoreGenerator = (props: Record<string, unknown>, railsContext: RailsContext) => Store; type ServerRenderHashRenderedHtml = { componentHtml: string; [key: string]: string; }; interface ServerRenderResult { renderedHtml?: string | ServerRenderHashRenderedHtml; redirectLocation?: { pathname: string; search: string; }; routeError?: Error; error?: Error; } type CreateReactOutputSyncResult = ServerRenderResult | ReactElement<unknown>; type CreateReactOutputAsyncResult = Promise<string | ServerRenderHashRenderedHtml | ReactElement<unknown>>; type CreateReactOutputResult = CreateReactOutputSyncResult | CreateReactOutputAsyncResult; type RenderFunctionSyncResult = ReactComponent | ServerRenderResult; type RenderFunctionAsyncResult = Promise<string | ServerRenderHashRenderedHtml | ReactComponent>; type RenderFunctionResult = RenderFunctionSyncResult | RenderFunctionAsyncResult; type StreamableComponentResult = ReactElement | Promise<ReactElement | string>; /** * Render-functions are used to create dynamic React components or server-rendered HTML with side effects. * They receive two arguments: props and railsContext. * * @param props - The component props passed to the render function * @param railsContext - The Rails context object containing environment information * @returns A string, React component, React element, or a Promise resolving to a string * * @remarks * To distinguish a render function from a React Function Component: * 1. Ensure it accepts two parameters (props and railsContext), even if railsContext is unused, or * 2. Set the `renderFunction` property to `true` on the function object. * * If neither condition is met, it will be treated as a React Function Component, * and ReactDOMServer will attempt to render it. * * @example * // Option 1: Two-parameter function * const renderFunction = (props, railsContext) => { ... }; * * // Option 2: Using renderFunction property * const anotherRenderFunction = (props) => { ... }; * anotherRenderFunction.renderFunction = true; */ interface RenderFunction { (props?: any, railsContext?: RailsContext, domNodeId?: string): RenderFunctionResult; renderFunction?: true; } type ReactComponentOrRenderFunction = ReactComponent | RenderFunction; type PipeableOrReadableStream = PipeableStream | NodeJS.ReadableStream; export type { ReactComponentOrRenderFunction, ReactComponent, AuthenticityHeaders, RenderFunction, RenderFunctionResult, Store, StoreGenerator, CreateReactOutputResult, ServerRenderResult, ServerRenderHashRenderedHtml, CreateReactOutputSyncResult, CreateReactOutputAsyncResult, RenderFunctionSyncResult, RenderFunctionAsyncResult, StreamableComponentResult, PipeableOrReadableStream, }; export interface RegisteredComponent { name: string; component: ReactComponentOrRenderFunction; /** * Indicates if the registered component is a RenderFunction * @see RenderFunction for more details on its behavior and usage. */ renderFunction: boolean; isRenderer: boolean; } export type ItemRegistrationCallback<T> = (component: T) => void; interface Params { props?: Record<string, unknown>; railsContext?: RailsContext; domNodeId?: string; trace?: boolean; } export interface RenderParams extends Params { name: string; throwJsErrors: boolean; renderingReturnsPromises: boolean; } export interface RSCRenderParams extends Omit<RenderParams, 'railsContext'> { railsContext: RailsContextWithServerStreamingCapabilities; } export interface CreateParams extends Params { componentObj: RegisteredComponent; shouldHydrate?: boolean; } export interface ErrorOptions { e: Error & { fileName?: string; lineNumber?: string; }; name?: string; jsCode?: string; serverSide: boolean; } export type RenderingError = Pick<Error, 'message' | 'stack'>; export type FinalHtmlResult = string | ServerRenderHashRenderedHtml; export interface RenderResult { html: FinalHtmlResult | null; consoleReplayScript: string; hasErrors: boolean; renderingError?: RenderingError; isShellReady?: boolean; } export interface RSCPayloadChunk extends RenderResult { html: string; } export interface Root { render(children: ReactNode): void; unmount(): void; } export type RenderReturnType = void | Element | Component | Root; export interface ReactOnRailsOptions { /** Gives you debugging messages on Turbolinks events. */ traceTurbolinks?: boolean; /** Turbo (the successor of Turbolinks) events will be registered, if set to true. */ turbo?: boolean; /** Enable debug mode for detailed logging of React on Rails operations. */ debugMode?: boolean; /** Log component registration details including timing and size information. */ logComponentRegistration?: boolean; } export interface ReactOnRails { /** * Main entry point to using the react-on-rails npm package. This is how Rails will be able to * find you components for rendering. * @param components keys are component names, values are components */ register(components: Record<string, ReactComponentOrRenderFunction>): void; /** @deprecated Use registerStoreGenerators instead */ registerStore(stores: Record<string, StoreGenerator>): void; /** * Allows registration of store generators to be used by multiple React components on one Rails * view. Store generators are functions that take one arg, props, and return a store. Note that * the `setStore` API is different in that it's the actual store hydrated with props. * @param storeGenerators keys are store names, values are the store generators */ registerStoreGenerators(storeGenerators: Record<string, StoreGenerator>): void; /** * Allows retrieval of the store by name. This store will be hydrated by any Rails form props. * @param name * @param [throwIfMissing=true] When false, this function will return undefined if * there is no store with the given name. * @returns Redux Store, possibly hydrated */ getStore(name: string, throwIfMissing?: boolean): Store | undefined; /** * Get a store by name, or wait for it to be registered. */ getOrWaitForStore(name: string): Promise<Store>; /** * Get a store generator by name, or wait for it to be registered. */ getOrWaitForStoreGenerator(name: string): Promise<StoreGenerator>; /** * Set options for ReactOnRails, typically before you call `ReactOnRails.register`. * @see {ReactOnRailsOptions} */ setOptions(newOptions: Partial<ReactOnRailsOptions>): void; /** * Renders or hydrates the React element passed. In case React version is >=18 will use the root API. * @param domNode * @param reactElement * @param hydrate if true will perform hydration, if false will render * @returns {Root|ReactComponent|ReactElement|null} */ reactHydrateOrRender(domNode: Element, reactElement: ReactElement, hydrate: boolean): RenderReturnType; /** * Allow directly calling the page loaded script in case the default events that trigger React * rendering are not sufficient, such as when loading JavaScript asynchronously with TurboLinks. * More details can be found here: * https://github.com/shakacode/react_on_rails/blob/master/docs/additional-reading/turbolinks.md */ reactOnRailsPageLoaded(): Promise<void>; reactOnRailsComponentLoaded(domId: string): Promise<void>; reactOnRailsStoreLoaded(storeName: string): Promise<void>; /** * Returns CSRF authenticity token inserted by Rails csrf_meta_tags * @returns String or null */ authenticityToken(): string | null; /** * Returns headers with CSRF authenticity token and XMLHttpRequest * @param otherHeaders Other headers */ authenticityHeaders(otherHeaders: Record<string, string>): AuthenticityHeaders; } export type RSCPayloadStreamInfo = { stream: NodeJS.ReadableStream; props: unknown; componentName: string; }; export type RSCPayloadCallback = (streamInfo: RSCPayloadStreamInfo) => void; /** Contains the parts of the `ReactOnRails` API intended for internal use only. */ export interface ReactOnRailsInternal extends ReactOnRails { /** * Retrieve an option by key. * @param key * @returns option value */ option<K extends keyof ReactOnRailsOptions>(key: K): ReactOnRailsOptions[K] | undefined; /** * Allows retrieval of the store generator by name. This is used internally by ReactOnRails after * a Rails form loads to prepare stores. * @param name * @returns Redux Store generator function */ getStoreGenerator(name: string): StoreGenerator; /** * Allows saving the store populated by Rails form props. Used internally by ReactOnRails. */ setStore(name: string, store: Store): void; /** * Clears `hydratedStores` to avoid accidental usage of wrong store hydrated in a previous/parallel * request. */ clearHydratedStores(): void; /** * @example * ```js * ReactOnRails.render("HelloWorldApp", {name: "Stranger"}, "app"); * ``` * * Does this: * ```js * ReactDOM.render( * React.createElement(HelloWorldApp, {name: "Stranger"}), * document.getElementById("app") * ); * ``` * under React 16/17 and * ```js * const root = ReactDOMClient.createRoot(document.getElementById("app")); * root.render(React.createElement(HelloWorldApp, {name: "Stranger"})); * return root; * ``` * under React 18+. * * @param name Name of your registered component * @param props Props to pass to your component * @param domNodeId HTML ID of the node the component will be rendered at * @param [hydrate=false] Pass truthy to update server rendered HTML. Default is falsy * @returns {Root|ReactComponent|ReactElement} Under React 18+: the created React root * (see "What is a root?" in https://github.com/reactwg/react-18/discussions/5). * Under React 16/17: Reference to your component's backing instance or `null` for stateless components. */ render(name: string, props: Record<string, string>, domNodeId: string, hydrate?: boolean): RenderReturnType; /** * Get the component that you registered * @returns {name, component, renderFunction, isRenderer} */ getComponent(name: string): RegisteredComponent; /** * Get the component that you registered, or wait for it to be registered * @returns {name, component, renderFunction, isRenderer} */ getOrWaitForComponent(name: string): Promise<RegisteredComponent>; /** * Used by server rendering by Rails */ serverRenderReactComponent(options: RenderParams): null | string | Promise<RenderResult>; /** * Used by server rendering by Rails */ streamServerRenderedReactComponent(options: RenderParams): Readable; /** * Generates RSC payload, used by Rails */ serverRenderRSCReactComponent(options: RSCRenderParams): Readable; /** * Used by Rails to catch errors in rendering */ handleError(options: ErrorOptions): string | undefined; /** * Used by Rails server rendering to replay console messages. */ buildConsoleReplay(): string; /** * Get a Map containing all registered components. Useful for debugging. */ registeredComponents(): Map<string, RegisteredComponent>; /** * Get a Map containing all registered store generators. Useful for debugging. */ storeGenerators(): Map<string, StoreGenerator>; /** * Get a Map containing all hydrated stores. Useful for debugging. */ stores(): Map<string, Store>; /** * Reset options to default. */ resetOptions(): void; /** * Current options. */ options: ReactOnRailsOptions; /** * Indicates if the RSC bundle is being used. */ isRSCBundle: boolean; } export type RenderStateHtml = FinalHtmlResult | Promise<FinalHtmlResult>; export type RenderState = { result: null | RenderStateHtml; hasErrors: boolean; error?: RenderingError; }; export type StreamRenderState = Omit<RenderState, 'result'> & { result: null | Readable; isShellReady: boolean; }; export type RenderOptions = { componentName: string; domNodeId?: string; trace?: boolean; renderingReturnsPromises: boolean; }; //# sourceMappingURL=index.d.ts.map