Skip to content

Commit 0b8e470

Browse files
update docs
1 parent cfa2a4a commit 0b8e470

File tree

2 files changed

+75
-59
lines changed

2 files changed

+75
-59
lines changed

docs/javascript/render-functions.md

Lines changed: 74 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,21 @@
11
# React on Rails Render Functions: Usage Guide
22

3-
Based on the test file `serverRenderReactComponent.test.ts` and the existing documentation, I'll clarify how render functions work in React on Rails and how to use them with each Ruby helper method.
3+
This guide explains how render functions work in React on Rails and how to use them with Ruby helper methods.
44

55
## Types of Render Functions and Their Return Values
66

7-
Looking at the test file, render functions can return several types of values:
7+
Render functions can return several types of values:
88

9-
### 1. React Components (JSX)
9+
### 1. React Components
1010

1111
```jsx
1212
const MyComponent = (props, _railsContext) => {
13-
return <div>Hello {props.name}</div>;
13+
return () => <div>Hello {props.name}</div>;
1414
};
1515
```
1616

17+
> [!NOTE] Ensure to return a React component (a function or class) and not a React element (the result of calling `React.createElement` or JSX).
18+
1719
### 2. Objects with renderedHtml Property
1820

1921
```jsx
@@ -24,14 +26,16 @@ const MyComponent = (props, _railsContext) => {
2426
};
2527
```
2628

27-
### 3. Objects with Multiple HTML Properties
29+
### 3. Objects with renderedHtml as object containing componentHtml and other properties if needed
2830

2931
```jsx
3032
const MyComponent = (props, _railsContext) => {
3133
return {
32-
componentHtml: <div>Hello {props.name}</div>,
33-
title: `<title>${props.title}</title>`,
34-
metaTags: `<meta name="description" content="${props.description}" />`,
34+
renderedHtml: {
35+
componentHtml: <div>Hello {props.name}</div>,
36+
title: `<title>${props.title}</title>`,
37+
metaTags: `<meta name="description" content="${props.description}" />`,
38+
},
3539
};
3640
};
3741
```
@@ -45,59 +49,62 @@ const MyComponent = async (props, _railsContext) => {
4549
};
4650
```
4751

48-
### 5. Promises of Objects
52+
### 5. Promises of Objects containing componentHtml and other properties if needed
4953

5054
```jsx
5155
const MyComponent = async (props, _railsContext) => {
5256
const data = await fetchData();
5357
return {
5458
componentHtml: `<div>Hello ${data.name}</div>`,
59+
title: `<title>${data.title}</title>`,
60+
metaTags: `<meta name="description" content="${data.description}" />`,
5561
};
5662
};
5763
```
5864

59-
### 6. Redirect Information
65+
### 6. Promises of React Components
66+
67+
```jsx
68+
const MyComponent = async (props, _railsContext) => {
69+
const data = await fetchData();
70+
return () => <div>Hello {data.name}</div>;
71+
};
72+
```
73+
74+
### 7. Redirect Information
75+
76+
> [!NOTE] React on Rails will not handle the actual redirection to another page. It will just return an empty component and depend on the front end to handle the redirection when it renders the router on the front end.
6077
6178
```jsx
6279
const MyComponent = (props, _railsContext) => {
6380
return {
64-
redirectLocation: '/new-path',
65-
error: null,
66-
renderedHtml: null,
81+
redirectLocation: { pathname: '/new-path', search: '' },
6782
};
6883
};
6984
```
7085

71-
## Important Limitations Observed in Tests
86+
## Important Rendering Behavior
7287

73-
The test file reveals some important limitations:
88+
Based on tests in [serverRenderReactComponent.test.ts](https://github.com/shakacode/react_on_rails/blob/master/node_package/tests/serverRenderReactComponent.test.ts):
7489

75-
1. **Direct String Returns Don't Work** - Returning a raw HTML string directly from a render function causes an error. The test `doesn't render html string returned directly from render function` demonstrates this.
90+
1. **Direct String Returns Don't Work** - Returning a raw HTML string directly from a render function causes an error. Always wrap HTML strings in `{ renderedHtml: '...' }`.
7691

77-
2. **Non-Promise Objects Need renderedHtml** - Non-promise objects must include a `renderedHtml` property to be valid, as shown in the test `doesn't render object without renderedHtml property`.
92+
2. **Objects Require Specific Properties** - Non-promise objects must include a `renderedHtml` property to be valid when used with `react_component`.
7893

79-
3. **Async Functions Have Different Behavior** - Interestingly, the test `returns the object returned by async render function even if it doesn't have renderedHtml property` shows that Promise-returning functions can return objects without a `renderedHtml` property and they will still work.
94+
3. **Async Functions Support All Return Types** - Async functions can return React components, strings, or objects with any property structure due to special handling in the server renderer, but it doesn't support properties like redirectLocation and routingError that can be returned by sync render function.
8095

81-
## Ruby Helper Functions with Render Functions
96+
## Ruby Helper Functions
8297

8398
### 1. react_component
8499

85-
The `react_component` helper is used for rendering a single React component. It accepts various return types from render functions:
100+
The `react_component` helper renders a single React component in your view.
86101

87102
```ruby
88-
# Basic usage with a component
89103
<%= react_component("MyComponent", props: { name: "John" }) %>
90-
91-
# With a render function that returns JSX
92-
<%= react_component("MyRenderFunction", props: { name: "John" }) %>
93-
94-
# With a render function that returns an object with renderedHtml
95-
<%= react_component("MyRenderFunction", props: { name: "John" }) %>
96-
97-
# With a render function that returns a Promise
98-
<%= react_component("MyAsyncRenderFunction", props: { name: "John" }) %>
99104
```
100105
106+
This helper accepts render functions that return React components, objects with a `renderedHtml` property, or promises that resolve to React components, or strings.
107+
101108
#### When to use:
102109
103110
- When you need to render a single component
@@ -107,7 +114,7 @@ The `react_component` helper is used for rendering a single React component. It
107114
#### Not suitable for:
108115
109116
- When your render function returns an object with multiple HTML strings
110-
- When you need to insert content in different parts of the page
117+
- When you need to insert content in different parts of the page, such as meta tags & style tags
111118
112119
### 2. react_component_hash
113120
@@ -130,6 +137,8 @@ The `react_component_hash` helper is used when your render function returns an o
130137
</div>
131138
```
132139

140+
This helper accepts render functions that return objects with a `renderedHtml` property containing `componentHtml` and any other necessary properties. It also supports promises that resolve to objects with `componentHtml` and other properties if needed.
141+
133142
#### When to use:
134143

135144
- When your render function returns multiple HTML strings in an object
@@ -148,55 +157,47 @@ The `react_component_hash` helper is used when your render function returns an o
148157
- The object MUST include a `componentHtml` key
149158
- All other keys are optional and can be accessed in your Rails view
150159

151-
## Best Practices Based on the Tests
152-
153-
1. **Always Use Objects for Multiple HTML Parts**: If you need multiple HTML strings, return an object with named properties, and use `react_component_hash` to access them.
154-
155-
2. **Don't Return Raw HTML Strings**: Tests show that returning a raw HTML string directly causes errors - either use JSX or wrap in a `{ renderedHtml: '...' }` object.
156-
157-
3. **Async Functions Work with Objects**: For async render functions, you can return both strings and objects (with or without a `renderedHtml` property).
158-
159-
4. **Use Redirect Object Format**: For redirects, return an object with `{ redirectLocation, error, renderedHtml: null }`.
160-
161-
## Example: Different Return Types with Appropriate Helper Methods
160+
## Examples with Appropriate Helper Methods
162161

163162
### Return Type 1: React Component
164163

165164
```jsx
166165
// JavaScript
167-
const SimpleComponent = (props) => <div>Hello {props.name}</div>;
166+
const SimpleComponent = (props, _railsContext) => () => <div>Hello {props.name}</div>;
168167
ReactOnRails.register({ SimpleComponent });
169168
```
170169

171170
```erb
172-
<%# Ruby - Either helper works %>
171+
<%# Ruby %>
173172
<%= react_component("SimpleComponent", props: { name: "John" }) %>
174173
```
175174

176175
### Return Type 2: Object with renderedHtml
177176

178177
```jsx
179178
// JavaScript
180-
const RenderedHtmlComponent = (props) => {
179+
const RenderedHtmlComponent = (props, _railsContext) => {
181180
return { renderedHtml: `<div>Hello ${props.name}</div>` };
182181
};
183182
ReactOnRails.register({ RenderedHtmlComponent });
184183
```
185184

186185
```erb
187-
<%# Ruby - Either helper works %>
186+
<%# Ruby %>
188187
<%= react_component("RenderedHtmlComponent", props: { name: "John" }) %>
189188
```
190189

191-
### Return Type 3: Object with Multiple HTML Properties
190+
### Return Type 3: Object with componentHtml and other properties if needed
192191

193192
```jsx
194193
// JavaScript
195194
const HelmetComponent = (props) => {
196195
return {
197-
componentHtml: <div>Hello {props.name}</div>,
198-
title: `<title>${props.title}</title>`,
199-
metaTags: `<meta name="description" content="${props.description}" />`,
196+
renderedHtml: {
197+
componentHtml: <div>Hello {props.name}</div>,
198+
title: `<title>${props.title}</title>`,
199+
metaTags: `<meta name="description" content="${props.description}" />`,
200+
},
200201
};
201202
};
202203
HelmetComponent.renderFunction = true;
@@ -235,7 +236,7 @@ ReactOnRails.register({ AsyncStringComponent });
235236
<%= react_component("AsyncStringComponent", props: { dataUrl: "/api/data" }) %>
236237
```
237238

238-
### Return Type 5: Promise of Object
239+
### Return Type 5: Promise of Object containing componentHtml and other properties if needed
239240

240241
```jsx
241242
// JavaScript
@@ -265,28 +266,43 @@ ReactOnRails.register({ AsyncObjectComponent });
265266
</div>
266267
```
267268

268-
### Return Type 6: Redirect Object
269+
### Return Type 6: Promise of React Component
270+
271+
```jsx
272+
// JavaScript
273+
const AsyncReactComponent = async (props) => {
274+
const data = await fetchData();
275+
return () => <div>Hello {data.name}</div>;
276+
};
277+
AsyncReactComponent.renderFunction = true;
278+
ReactOnRails.register({ AsyncReactComponent });
279+
```
280+
281+
```erb
282+
<%# Ruby %>
283+
<%= react_component("AsyncReactComponent", props: { dataUrl: "/api/data" }) %>
284+
```
285+
286+
### Return Type 7: Redirect Object
269287

270288
```jsx
271289
const RedirectComponent = (props, railsContext) => {
272290
if (!railsContext.currentUser) {
273291
return {
274-
redirectLocation: { pathname: '/login', search: '' }, // Object with pathname and search
275-
error: null,
276-
renderedHtml: null, // Use renderedHtml for consistency
292+
redirectLocation: { pathname: '/login', search: '' },
277293
};
278294
}
279295
return {
280-
renderedHtml: <div>Welcome {railsContext.currentUser.name}</div>, // Use renderedHtml for consistency
296+
renderedHtml: <div>Welcome {railsContext.currentUser.name}</div>,
281297
};
282298
};
283299
RedirectComponent.renderFunction = true;
284300
ReactOnRails.register({ RedirectComponent });
285301
```
286302

287303
```erb
288-
<%# Ruby - Either helper works %>
304+
<%# Ruby %>
289305
<%= react_component("RedirectComponent") %>
290306
```
291307

292-
By understanding the different return types and which helper to use with each, you can create sophisticated server-rendered React components that fully integrate with your Rails views.
308+
By understanding these return types and which helper to use with each, you can create sophisticated server-rendered React components that fully integrate with your Rails views.

node_package/tests/serverRenderReactComponent.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ describe('serverRenderReactComponent', () => {
7777

7878
it('serverRenderReactComponent renders html renderedHtml property', () => {
7979
const expectedHtml = '<div>Hello</div>';
80-
const X3: RenderFunction = (): { renderedHtml: string } => ({
80+
const X3: RenderFunction = () => ({
8181
renderedHtml: expectedHtml,
8282
});
8383
X3.renderFunction = true;

0 commit comments

Comments
 (0)