Skip to content

Move JS API docs to the interface #1716

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Mar 27, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 2 additions & 4 deletions docs/api/javascript-api.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ If you are using [jquery-ujs](https://github.com/rails/jquery-ujs) for AJAX call

## API

The best source of docs is the main [ReactOnRails.ts](https://github.com/shakacode/react_on_rails/blob/master/node_package/src/ReactOnRails.ts) file. Here's a quick summary. No guarantees that this won't be outdated!
The best source of docs is the `interface ReactOnRails` in [types/index.ts](https://github.com/shakacode/react_on_rails/blob/master/node_package/src/types/index.ts). Here's a quick summary. No guarantees that this won't be outdated!

```js
/**
Expand All @@ -44,7 +44,7 @@ register(components);
* the setStore API is different in that it's the actual store hydrated with props.
* @param stores (key is store name, value is the store generator)
*/
registerStore(stores);
registerStoreGenerators(storesGenerators);

/**
* Allows retrieval of the store by name. This store will be hydrated by any Rails form props.
Expand Down Expand Up @@ -85,14 +85,12 @@ reactOnRailsPageLoaded();
* Returns CSRF authenticity token inserted by Rails csrf_meta_tags
* @returns String or null
*/

authenticityToken();

/**
* Returns header with csrf authenticity token and XMLHttpRequest
* @param {*} other headers
* @returns {*} header
*/

authenticityHeaders((otherHeaders = {}));
```
150 changes: 4 additions & 146 deletions node_package/src/ReactOnRails.client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import type {
AuthenticityHeaders,
Store,
StoreGenerator,
ReactOnRailsOptions,
} from './types';
import reactHydrateOrRender from './reactHydrateOrRender';

Expand All @@ -40,11 +41,7 @@ const DEFAULT_OPTIONS = {

ctx.ReactOnRails = {
options: {},
/**
* 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 (key is component name, value is component)
*/

register(components: Record<string, ReactComponentOrRenderFunction>): void {
ComponentRegistry.register(components);
},
Expand All @@ -53,12 +50,6 @@ ctx.ReactOnRails = {
this.registerStoreGenerators(stores);
},

/**
* 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 {
if (!storeGenerators) {
throw new Error(
Expand All @@ -70,55 +61,23 @@ ctx.ReactOnRails = {
StoreRegistry.register(storeGenerators);
},

/**
* Allows retrieval of the store by name. This store will be hydrated by any Rails form props.
* Pass optional param throwIfMissing = false if you want to use this call to get back null if the
* store with name is not registered.
* @param name
* @param throwIfMissing Defaults to true. Set to false to have this call return undefined if
* there is no store with the given name.
* @returns Redux Store, possibly hydrated
*/
getStore(name: string, throwIfMissing = true): Store | undefined {
return StoreRegistry.getStore(name, throwIfMissing);
},

/**
* Get a store by name, or wait for it to be registered.
* @param name
* @returns Promise<Store>
*/
getOrWaitForStore(name: string): Promise<Store> {
return StoreRegistry.getOrWaitForStore(name);
},

/**
* Get a store generator by name, or wait for it to be registered.
* @param name
* @returns Promise<StoreGenerator>
*/
getOrWaitForStoreGenerator(name: string): Promise<StoreGenerator> {
return StoreRegistry.getOrWaitForStoreGenerator(name);
},

/**
* 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 {
return reactHydrateOrRender(domNode, reactElement, hydrate);
},

/**
* Set options for ReactOnRails, typically before you call ReactOnRails.register
* Available Options:
* `traceTurbolinks: true|false Gives you debugging messages on Turbolinks events
* `turbo: true|false Turbo (the follower of Turbolinks) events will be registered, if set to true.
*/
setOptions(newOptions: { traceTurbolinks?: boolean; turbo?: boolean }): void {
setOptions(newOptions: Partial<ReactOnRailsOptions>): void {
if (typeof newOptions.traceTurbolinks !== 'undefined') {
this.options.traceTurbolinks = newOptions.traceTurbolinks;

Expand All @@ -138,12 +97,6 @@ ctx.ReactOnRails = {
}
},

/**
* 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() {
return ClientStartup.reactOnRailsPageLoaded();
},
Expand All @@ -156,21 +109,10 @@ ctx.ReactOnRails = {
return hydrateStore(storeName);
},

/**
* Returns CSRF authenticity token inserted by Rails csrf_meta_tags
* @returns String or null
*/

authenticityToken(): string | null {
return Authenticity.authenticityToken();
},

/**
* Returns header with csrf authenticity token and XMLHttpRequest
* @param otherHeaders Other headers
* @returns {*} header
*/

authenticityHeaders(otherHeaders: Record<string, string> = {}): AuthenticityHeaders {
return Authenticity.authenticityHeaders(otherHeaders);
},
Expand All @@ -179,66 +121,22 @@ ctx.ReactOnRails = {
// INTERNALLY USED APIs
// /////////////////////////////////////////////////////////////////////////////

/**
* Retrieve an option by key.
* @param key
* @returns option value
*/
option(key: string): string | number | boolean | undefined {
option<K extends keyof ReactOnRailsOptions>(key: K): ReactOnRailsOptions[K] | undefined {
return this.options[key];
},

/**
* 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 {
return StoreRegistry.getStoreGenerator(name);
},

/**
* Allows saving the store populated by Rails form props. Used internally by ReactOnRails.
* @returns Redux Store, possibly hydrated
*/
setStore(name: string, store: Store): void {
StoreRegistry.setStore(name, store);
},

/**
* Clears hydratedStores to avoid accidental usage of wrong store hydrated in previous/parallel
* request.
*/
clearHydratedStores(): void {
StoreRegistry.clearHydratedStores();
},

/**
* @example
* 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
* @param hydrate 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 {
const componentObj = ComponentRegistry.get(name);
const reactElement = createReactOutput({ componentObj, props, domNodeId });
Expand All @@ -250,88 +148,48 @@ ctx.ReactOnRails = {
);
},

/**
* Get the component that you registered
* @param name
* @returns {name, component, renderFunction, isRenderer}
*/
getComponent(name: string): RegisteredComponent {
return ComponentRegistry.get(name);
},

/**
* Get the component that you registered, or wait for it to be registered
* @param name
* @returns {name, component, renderFunction, isRenderer}
*/
getOrWaitForComponent(name: string): Promise<RegisteredComponent> {
return ComponentRegistry.getOrWaitForComponent(name);
},

/**
* Used by server rendering by Rails
* @param options
*/
serverRenderReactComponent(): null | string | Promise<RenderResult> {
throw new Error(
'serverRenderReactComponent is not available in "react-on-rails/client". Import "react-on-rails" server-side.',
);
},

/**
* Used by server rendering by Rails
* @param options
*/
streamServerRenderedReactComponent() {
throw new Error(
'streamServerRenderedReactComponent is only supported when using a bundle built for Node.js environments',
);
},

/**
* Generates RSC payload, used by Rails
*/
serverRenderRSCReactComponent() {
throw new Error('serverRenderRSCReactComponent is supported in RSC bundle only.');
},

/**
* Used by Rails to catch errors in rendering
* @param options
*/
handleError(): string | undefined {
throw new Error(
'handleError is not available in "react-on-rails/client". Import "react-on-rails" server-side.',
);
},

/**
* Used by Rails server rendering to replay console messages.
*/
buildConsoleReplay(): string {
return buildConsoleReplay();
},

/**
* Get an Object containing all registered components. Useful for debugging.
* @returns {*}
*/
registeredComponents(): Map<string, RegisteredComponent> {
return ComponentRegistry.components();
},

/**
* Get an Object containing all registered store generators. Useful for debugging.
* @returns {*}
*/
storeGenerators(): Map<string, StoreGenerator> {
return StoreRegistry.storeGenerators();
},

/**
* Get an Object containing all hydrated stores. Useful for debugging.
* @returns {*}
*/
stores(): Map<string, Store> {
return StoreRegistry.stores();
},
Expand Down
8 changes: 0 additions & 8 deletions node_package/src/ReactOnRails.full.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,8 @@ if (typeof window !== 'undefined') {
);
}

/**
* Used by Rails to catch errors in rendering
* @param options
*/
Client.handleError = (options: ErrorOptions): string | undefined => handleError(options);

/**
* Used by server rendering by Rails
* @param options
*/
Client.serverRenderReactComponent = (options: RenderParams): null | string | Promise<RenderResult> =>
serverRenderReactComponent(options);

Expand Down
2 changes: 1 addition & 1 deletion node_package/src/context.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { ReactOnRails as ReactOnRailsType, RailsContext } from './types';
import type { ReactOnRailsInternal as ReactOnRailsType, RailsContext } from './types';

declare global {
interface Window {
Expand Down
Loading
Loading