-
-
Notifications
You must be signed in to change notification settings - Fork 634
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
Add Support for Async Render Functions Returning React Components #1720
Add Support for Async Render Functions Returning React Components #1720
Conversation
WalkthroughThe update introduces a new documentation guide on render functions in React on Rails and adjusts multiple source modules to improve type flexibility, error handling, and asynchronous processing during server rendering. Changes include updates to method signatures, new helper functions, refined type definitions, and expanded tests. Additionally, modifications to streaming error reporting and ESLint configuration help streamline rendering workflows. Changes
Sequence Diagram(s)sequenceDiagram
participant C as Client
participant S as Streaming Renderer
participant P as Promise Resolver
participant R as ReactDOMServer
participant E as Error Handler
C->>S: Request streaming render
S->>P: Resolve reactRenderingResult
P-->>S: Return resolved value (React element or string)
alt Resolved value is a valid React element
S->>R: Render element via processReactElement
R-->>S: Return rendered HTML
S-->>C: Stream rendered HTML
else Resolved value is an error or string
S->>E: Invoke reportError/sendErrorHtml
E-->>S: Provide error HTML response
S-->>C: Stream error HTML
end
Possibly related PRs
Suggested reviewers
Poem
✨ Finishing Touches
🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments. CodeRabbit Commands (Invoked using PR comments)
Other keywords and placeholders
CodeRabbit Configuration File (
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
🧹 Nitpick comments (4)
docs/javascript/render-functions.md (2)
101-110
: Remove trailing colons in headingsThe headings "When to use:" and similar headings throughout the document have trailing punctuation which should be removed according to markdown best practices.
-#### When to use: +#### When to use🧰 Tools
🪛 markdownlint-cli2 (0.17.2)
101-101: Trailing punctuation in heading
Punctuation: ':'(MD026, no-trailing-punctuation)
107-107: Trailing punctuation in heading
Punctuation: ':'(MD026, no-trailing-punctuation)
147-149
: Consider using consistent formatting for requirementsThe requirements section uses hyphens that could be misinterpreted as "and" vs "or". Consider using consistent formatting or explicit conjunctions.
-#### Requirements: +#### Requirements -The render function MUST return an object -The object MUST include a `componentHtml` key -All other keys are optional and can be accessed in your Rails view +The render function MUST return an object. +The object MUST include a `componentHtml` key. +All other keys are optional and can be accessed in your Rails view.🧰 Tools
🪛 LanguageTool
[misspelling] ~147-~147: Did you mean “and”?
Context: ...nts: - The render function MUST return an object - The object MUST include a `com...(AN_AND)
node_package/src/serverRenderReactComponent.ts (1)
47-56
: Log the original error details for better debugging.This block catches rendering errors and logs a message. Logging the error’s stack (or relevant details) can be invaluable for diagnosing issues unrelated to double render calls.
Add the original error details alongside the custom message:
} catch (error) { console.error( `Invalid call to renderToString. Possibly you have a renderFunction, a function that already calls renderToString, that takes one parameter. You need to add an extra unused parameter to identify this function as a renderFunction and not a simple React Function Component.`, + error ); throw error; }
node_package/tests/streamServerRenderedReactComponent.test.jsx (1)
63-97
: Use ofcomponentType
adds clarity to test coverage.This switch-based approach for registering different component types effectively tests all sync/async error and success paths. Consider refactoring repeated error-throwing stubs to reduce duplication, but overall functionality looks good.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (11)
docs/javascript/render-functions.md
(1 hunks)node_package/src/ReactOnRailsRSC.ts
(2 hunks)node_package/src/createReactOutput.ts
(2 hunks)node_package/src/handleError.ts
(1 hunks)node_package/src/isServerRenderResult.ts
(2 hunks)node_package/src/serverRenderReactComponent.ts
(2 hunks)node_package/src/streamServerRenderedReactComponent.ts
(4 hunks)node_package/src/types/index.ts
(1 hunks)node_package/tests/serverRenderReactComponent.test.js
(0 hunks)node_package/tests/serverRenderReactComponent.test.ts
(1 hunks)node_package/tests/streamServerRenderedReactComponent.test.jsx
(6 hunks)
💤 Files with no reviewable changes (1)
- node_package/tests/serverRenderReactComponent.test.js
🧰 Additional context used
🧬 Code Definitions (5)
node_package/src/ReactOnRailsRSC.ts (2)
node_package/src/types/index.ts (1)
RSCRenderParams
(143-145)node_package/src/loadReactClientManifest.ts (1)
loadReactClientManifest
(7-24)
node_package/src/isServerRenderResult.ts (1)
node_package/src/types/index.ts (3)
CreateReactOutputResult
(105-105)RenderFunctionResult
(102-102)ServerRenderResult
(106-106)
node_package/src/streamServerRenderedReactComponent.ts (3)
node_package/src/types/index.ts (1)
RenderParams
(137-141)node_package/src/ReactOnRails.client.ts (1)
handleError
(302-306)node_package/src/serverRenderUtils.ts (1)
convertToError
(20-22)
node_package/src/createReactOutput.ts (1)
node_package/src/isServerRenderResult.ts (2)
isServerRenderHash
(3-12)isPromise
(14-18)
node_package/tests/serverRenderReactComponent.test.ts (1)
node_package/src/types/index.ts (6)
RenderParams
(137-141)RenderResult
(163-169)RenderFunction
(101-101)RailsContext
(17-35)ServerRenderResult
(106-106)RenderFunctionResult
(102-102)
🪛 GitHub Actions: Lint JS and Ruby
node_package/tests/serverRenderReactComponent.test.ts
[error] 1-1: Parsing error: /home/runner/work/react_on_rails/react_on_rails/node_package/tests/serverRenderReactComponent.test.ts was not found by the project service. Consider either including it in the tsconfig.json or including it in allowDefaultProject.
🪛 LanguageTool
docs/javascript/render-functions.md
[uncategorized] ~79-~79: Use a comma before ‘and’ if it connects two independent clauses (unless they are closely connected and short).
Context: ...bjects without a renderedHtml
property and they will still work. ## Ruby Helper F...
(COMMA_COMPOUND_SENTENCE)
[style] ~136-~136: You have already used this phrasing in nearby sentences. Consider replacing it to add variety to your writing.
Context: ...le HTML strings in an object - When you need to insert rendered content in different pa...
(REP_NEED_TO_VB)
[misspelling] ~147-~147: Did you mean “and”?
Context: ...nts: - The render function MUST return an object - The object MUST include a `com...
(AN_AND)
[uncategorized] ~155-~155: Loose punctuation mark.
Context: ...es errors - either use JSX or wrap in a { renderedHtml: '...' }
object. 3. **Async Functions Work wit...
(UNLIKELY_OPENING_PUNCTUATION)
🪛 markdownlint-cli2 (0.17.2)
docs/javascript/render-functions.md
101-101: Trailing punctuation in heading
Punctuation: ':'
(MD026, no-trailing-punctuation)
107-107: Trailing punctuation in heading
Punctuation: ':'
(MD026, no-trailing-punctuation)
133-133: Trailing punctuation in heading
Punctuation: ':'
(MD026, no-trailing-punctuation)
140-140: Trailing punctuation in heading
Punctuation: ':'
(MD026, no-trailing-punctuation)
145-145: Trailing punctuation in heading
Punctuation: ':'
(MD026, no-trailing-punctuation)
🔇 Additional comments (49)
node_package/src/handleError.ts (2)
63-66
: Good defensive programming to handle undefined renderToStringThis change adds a check to ensure
ReactDOMServer.renderToString
is a function before attempting to call it. This prevents potential runtime errors ifrenderToString
is unavailable in certain environments or scenarios.
63-66
: Good defensive programming technique!This added check ensures the code handles situations where
ReactDOMServer.renderToString
might not be available as a function, preventing potential runtime errors. This makes the error handling more robust across different environments.node_package/src/createReactOutput.ts (8)
5-23
: Good extraction of render function result handling logicThe new
createReactElementFromRenderFunctionResult
function cleanly encapsulates the logic for processing render function results. The warning about returning JSX directly helps guide developers toward the proper pattern that will work with React Hooks in v13.
70-74
: Clean up of isServerRenderHash handlingThe code now cleanly handles the server render hash case without type assertions, improving readability and type safety.
76-89
: Enhanced promise handling for render function resultsThe promise handling now properly differentiates between different result types (strings, functions, or other values) and processes each appropriately. This makes the function more robust when handling asynchronous render function results.
91-91
: Simplified code with extracted functionUsing the new
createReactElementFromRenderFunctionResult
function here simplifies the main flow and improves maintainability.
5-23
: Well-structured helper function with good warning messageThe new
createReactElementFromRenderFunctionResult
function provides a clean way to handle different return types from render functions. The warning message is especially valuable as it guides developers toward best practices for React Hooks compatibility in future versions.
70-74
: Clean type handlingThe simplified check for
isServerRenderHash
improves code readability by removing unnecessary type assertions.
76-89
: Enhanced promise handling for async render functionsThis improved logic for handling promises correctly processes different types of resolved values:
- Strings are returned directly
- Functions are processed using the new helper function
- Other values are stringified
This enhancement provides robust support for asynchronous render functions, which is the core objective of this PR.
91-91
: Consistent use of helper functionUsing the new helper function here maintains consistency throughout the codebase.
node_package/src/ReactOnRailsRSC.ts (4)
24-26
: Function signature expanded to support async render functionsThe function now accepts
reactRenderingResult
of typeReactElement | Promise<ReactElement | string>
instead of justReactElement
. This enhancement allows the component to handle both synchronous and asynchronous rendering results.
37-38
: Optimized parallel loading of manifest and rendering resultUsing
Promise.all
to concurrently load the React client manifest and resolve the rendering result is an effective optimization. This approach avoids sequential processing, potentially improving performance.
24-26
: Improved function signature for async supportUpdating the parameter type from
ReactElement
toReactElement | Promise<ReactElement | string>
broadens the range of valid inputs, making the function more flexible and aligned with modern React patterns involving async operations.
37-38
: Performance optimization with Promise.allUsing
Promise.all
to concurrently load the React client manifest and resolve the rendering result is more efficient than sequential processing. This enhances performance when dealing with async operations, especially if either operation takes significant time.docs/javascript/render-functions.md (4)
1-3
: Excellent documentation title and introductionThe title clearly indicates this is a usage guide for render functions in React on Rails, which helps users immediately understand the document's purpose.
5-69
: Comprehensive coverage of render function return typesThe documentation thoroughly covers all possible return types from render functions with clear examples for each case. This is extremely helpful for developers to understand the options available to them.
71-80
: Valuable information about limitationsHighlighting the important limitations observed in tests provides crucial information for developers to avoid common pitfalls when using render functions.
🧰 Tools
🪛 LanguageTool
[uncategorized] ~79-~79: Use a comma before ‘and’ if it connects two independent clauses (unless they are closely connected and short).
Context: ...bjects without arenderedHtml
property and they will still work. ## Ruby Helper F...(COMMA_COMPOUND_SENTENCE)
161-290
: Excellent examples with both JavaScript and Ruby codeThe comprehensive examples showing both the JavaScript render functions and their corresponding Ruby usage provide a complete picture for developers. This dual-language approach significantly improves the documentation's utility.
node_package/src/serverRenderReactComponent.ts (2)
59-59
: Signature update for async returns looks good.Expanding
Promise<string>
toPromise<string | ReactElement>
aligns with supporting asynchronous React element creation.
71-75
:✅ Verification successful
Confirm promise type assumptions.
This logic assumes promiseResult is always a string or a valid ReactElement. If there’s any scenario where it could return a different type (e.g., null, number, or object), this code would fail. Ensure upstream calls respect this contract.
Check all call sites to confirm only strings or
ReactElement
s are ever returned.
🏁 Script executed:
#!/bin/bash rg -A 3 'processPromise'Length of output: 710
Confirmed promise type contract.
After verifying with the call sites and type definitions, it’s clear that the contract enforced by
processPromise
guarantees that the resolved value is either a string or a valid ReactElement. The upstream code ensures that no other types (e.g., null, number, or object) will be returned, so the logic in these lines is safe.
- File:
node_package/src/serverRenderReactComponent.ts
(Lines 71-75)- Verification: The function signature for
processPromise
enforces a return type ofPromise<string> | string
, ensuring promiseResult is either a string or a ReactElement.- Action: No further changes are required unless new types are introduced upstream.
node_package/src/isServerRenderResult.ts (2)
1-5
: Broadening function signatures for added flexibility.Including
RenderFunctionResult
ensures the type guard can handle all new async/sync return possibilities without regressions.
15-15
: Promise type signature update is consistent.Allowing
RenderFunctionResult
inisPromise
matches the broader support for async returns within render functions.node_package/src/types/index.ts (4)
45-48
: Introduction ofServerRenderHashRenderedHtml
is a clear approach.This structure is well-defined and helps clarify the presence of a
componentHtml
field with optional extra keys.
51-51
: Union type onrenderedHtml
broadens support.Enabling either a string or the new
ServerRenderHashRenderedHtml
type aligns with supporting complex SSR outputs.
57-57
: ExtendedCreateReactOutputResult
type for async React elements.Including
Promise<string | ReactElement>
accommodates asynchronous rendering seamlessly.
59-62
:RenderFunctionResult
now correctly accounts for async/sync returns.Supporting promises of string,
ServerRenderHashRenderedHtml
, orReactComponent
unifies multiple SSR return paths into one flexible type.node_package/tests/streamServerRenderedReactComponent.test.jsx (13)
118-118
: Using.once('end')
prevents accidental double invocation.This is a safer way to handle the stream's end event.
137-137
: Same change as line 118.
153-153
: Same change as line 118.
169-169
: Same change as line 118.
173-173
: Allowing ≥ 2 chunks ensures both shell and async content.This assertion confirms that at least two chunks are streamed before completion.
179-179
: Error-check for async content is correct.Testing for the exact error message ensures the fallback logic is triggered properly.
200-200
: Similar test logic for error messages.
206-224
: Tests for streaming a resolved React component confirm correct shell + content flow.This scenario effectively validates the approach for both immediate and eventually returned React elements.
226-240
: Covers sync errors in render function thoroughly.Ensures we see the correct fallback path for immediate errors.
242-260
: Covers async errors in render function thoroughly.Successfully tests the fallback for promise-based errors.
262-282
: Ensures that errors thrown during render function execution are captured.This test confirms the user sees a coherent error message.
284-307
: Verifies error emission behavior when throwJsErrors is true.Properly checks that an error event is emitted when the function fails.
309-334
: Tests for streaming a plain string from a promise.Great coverage to confirm that plain strings also work in async rendering contexts.
node_package/tests/serverRenderReactComponent.test.ts (5)
1-2
: Include this new test file in the TypeScript project configuration.
The lint failure indicates this file is not recognized by the TypeScript compiler. To fix it, please add or reference it in the appropriatetsconfig.json
so that it's included in the project.Would you like me to generate a script that searches for all references to this file in your repository and queries the
tsconfig.json
entries to verify inclusion?🧰 Tools
🪛 GitHub Actions: Lint JS and Ruby
[error] 1-1: Parsing error: /home/runner/work/react_on_rails/react_on_rails/node_package/tests/serverRenderReactComponent.test.ts was not found by the project service. Consider either including it in the tsconfig.json or including it in allowDefaultProject.
39-58
: Basic rendering test looks good.
This test effectively covers a straightforward scenario of rendering a registered component and verifying the correcthtml
andhasErrors
flags.
108-127
: Good negative test for invalid rendered object.
Verifies that returning an object without arenderedHtml
property triggers an error, which is the intended behavior.
235-250
: Confirm coverage for a promise that returns a function.
Here the promise resolves to a function that itself returns a React element. Consider covering edge cases, such as a promise that resolves after a delay or that rejects mid-render, to ensure resilience of streaming.If you'd like, I can generate an additional test that introduces an artificial delay or rejection path.
252-276
: Error-handling test ensures graceful failure.
This test appropriately checks that an invalid scenario triggers a user-facing error message (Exception in rendering!
). The coverage for error states seems thorough.node_package/src/streamServerRenderedReactComponent.ts (5)
7-7
: Helper import usage is consistent.
TheisServerRenderHash
import is properly utilized below to guard against unsupported server render hashes before streaming.
124-127
: Enhanced flexibility for async rendering.
Expanding the parameter toReactElement | Promise<ReactElement | string>
correctly accommodates asynchronous or string-based results. Please ensure all upstream calls handle these types without assumptions of synchronicity.
138-152
: Centralized error handling approach is good practice.
reportError
andsendErrorHtml
keep concerns clearly separated, helping maintain clarity for the streaming logic and potential expansions (e.g., logging).
153-185
: Promise resolution logic looks robust.
The code properly handles different result types, logging a console error if a string is returned, then writing that string to the stream. This is consistent with your design choice that raw strings aren’t the intended final behavior but can still be captured.
219-233
: Fallback error-handling in the catch block.
Wrapping the streaming logic within atry/catch
ensures you can handle both runtime and synchronous setup errors gracefully. The final fallback to push error HTML is well-managed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please also figure out the necessary commit steps to get git to recognize that serverRenderReactComponent.test.js
has not been deleted, but has been renamed to serverRenderReactComponent.test.ts
.
Try renaming first and then making changes in a separate commit.
Having git recognize renamed files makes code reviews a lot easier for those files.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🧹 Nitpick comments (2)
node_package/tests/serverRenderReactComponent.test.ts (2)
32-36
: Consider adding teardown to clean up after tests.While the
beforeEach
clears the ComponentRegistry before each test, there's no corresponding cleanup after the tests complete. Consider adding anafterAll
to ensure a clean state after the test suite runs.beforeEach(() => { ComponentRegistry.components().clear(); }); + + afterAll(() => { + ComponentRegistry.components().clear(); + });
1-279
: Add tests for component promise rejection.While the test suite covers many scenarios, it doesn't test what happens when a Promise is rejected. Consider adding a test case for a render function that returns a rejected Promise to ensure errors are properly handled.
Here's a suggested test implementation:
it('handles rejected promises from render functions', async () => { const errorMessage = 'Async error in render function'; const X9: RenderFunction = (_props: unknown, _railsContext?: RailsContext) => Promise.reject(new Error(errorMessage)); ComponentRegistry.register({ X9 }); const renderResult = serverRenderReactComponent({ name: 'X9', domNodeId: 'myDomId', trace: false, throwJsErrors: false, renderingReturnsPromises: true, }); assertIsPromise(renderResult); const result = await renderResult; expect(result.hasErrors).toBeTruthy(); expect(result.html).toContain('Exception in rendering!'); expect(result.html).toContain(errorMessage); });
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
eslint.config.ts
(1 hunks)node_package/tests/serverRenderReactComponent.test.ts
(1 hunks)
🧰 Additional context used
🪛 GitHub Actions: Lint JS and Ruby
eslint.config.ts
[error] Parsing error: allowDefaultProject glob 'node_package/tests//*.ts' contains a disallowed ''.
node_package/tests/serverRenderReactComponent.test.ts
[error] Parsing error: allowDefaultProject glob 'node_package/tests//*.ts' contains a disallowed ''.
⏰ Context from checks skipped due to timeout of 90000ms (2)
- GitHub Check: dummy-app-integration-tests (newest)
- GitHub Check: dummy-app-integration-tests (oldest)
🔇 Additional comments (12)
eslint.config.ts (1)
148-148
:❓ Verification inconclusive
Fix the glob pattern in allowDefaultProject.
The pipeline is failing because the glob pattern
node_package/tests/**/*.ts
contains a disallowed double asterisk ('**'). According to ESLint's configuration, double asterisks are not permitted in theallowDefaultProject
array.- allowDefaultProject: ['eslint.config.ts', 'knip.ts', 'node_package/tests/**/*.ts'], + allowDefaultProject: ['eslint.config.ts', 'knip.ts', 'node_package/tests/*.ts'],If you need to include TypeScript files in subdirectories, consider specifying them individually or using a different configuration approach that allows recursive patterns.
🏁 Script executed:
#!/bin/bash # Check if there are TypeScript files in subdirectories of node_package/tests find node_package/tests -name "*.ts" | grep -v "^node_package/tests/[^/]*\.ts$" | head -n 5Length of output: 93
Update Glob Pattern in ESLint Configuration
The ESLint rules disallow the use of double asterisks (
**
) in theallowDefaultProject
array. The patch replaces the problematic pattern with a single asterisk to match only files directly undernode_package/tests
, which resolves the pipeline failure.Before finalizing this change, please verify whether any TypeScript files are intentionally placed in subdirectories of
node_package/tests
. If such files exist, you may need to list them explicitly or adopt a different configuration approach to avoid inadvertently excluding them.- allowDefaultProject: ['eslint.config.ts', 'knip.ts', 'node_package/tests/**/*.ts'], + allowDefaultProject: ['eslint.config.ts', 'knip.ts', 'node_package/tests/*.ts'],node_package/tests/serverRenderReactComponent.test.ts (11)
1-31
: Good setup for type safety in tests.The type assertion functions and helper for matching objects provide strong type safety in the tests. This is especially important for testing TypeScript code that handles promises and different return types.
37-57
: LGTM: Effective test for basic component rendering.This test case properly verifies the core functionality of rendering a registered React component, with appropriate assertions on the HTML output and error state.
59-82
: LGTM: Good error handling test.This test effectively verifies that errors thrown during component rendering are properly caught and reported in the output.
84-106
: LGTM: Valid test for renderedHtml property.This test correctly verifies that objects with a
renderedHtml
property are processed correctly.
108-127
: LGTM: Good test for invalid object returns.This test properly verifies error handling when a render function returns an object without the required
renderedHtml
property.
129-161
: Well-documented edge case test.The test is preceded by detailed documentation explaining a subtle behavior when rendering functions return strings directly. This is valuable both for test clarity and as documentation for developers.
163-184
: LGTM: Good test for async rendering with string HTML.This test correctly verifies the handling of promises that resolve to string HTML, which is a key part of the new async rendering support being added.
186-210
: LGTM: Well-documented test for async object returns.The detailed comments explain an important behavior of the component and potential issues. The test effectively validates this behavior.
212-233
: LGTM: Good test for react_component_hash helper compatibility.This test demonstrates how async render functions can be used with the
react_component_hash
helper, providing a real-world usage pattern.
235-250
: LGTM: Good test for async component rendering.This test correctly verifies that an async function returning a React component works properly, which is a core part of the PR's objective.
252-277
: LGTM: Proper error handling test for renderer objects.This test verifies that attempting to render a renderer object (rather than a component) results in an appropriate error.
98eed42
to
7c4903d
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🔭 Outside diff range comments (1)
node_package/src/serverRenderReactComponent.ts (1)
30-43
: 🛠️ Refactor suggestionImproved handling of
renderedHtml
with null safety.The changes improve the handling of
renderedHtml
by:
- Removing the explicit type constraint
- Using the nullish coalescing operator (
??
) to handle undefined valuesHowever, this handling still doesn't fully address the concern from the previous review about explicitly checking for
null
values.Consider implementing more explicit handling:
htmlResult = result.renderedHtml; + + // Line 44 + if (htmlResult === null || htmlResult === undefined) { + htmlResult = ''; + } else if (typeof htmlResult !== 'string') { + htmlResult = JSON.stringify(htmlResult); + }This would provide more predictable behavior and prevent potential issues downstream.
Also applies to: 45-46
🧹 Nitpick comments (10)
docs/javascript/render-functions.md (10)
1-4
: Introduction Clarity and Test File Link Reference
The introduction is clear and well laid out. Consider adding a hyperlink for the referenced test file (e.g.[serverRenderReactComponent.test.ts](./serverRenderReactComponent.test.ts)
) to improve navigability and help readers quickly locate the source tests.
27-37
: Example Section for Objects with Multiple HTML Properties
Examples demonstrate multiple HTML parts well. Consider if a brief note should be added to explain when and why one might return multiple keys (such as for SEO purposes).
73-79
: Enhance Consistency in Limitation Descriptions
In the "Important Limitations Observed in Tests" section, consider:
- Consistently capitalizing “HTML” (instead of “html”) for clarity.
- Revising the phrasing for the async functions limitation by inserting an article (e.g. “a Promise‐returning function…”).
- Adding a comma before “and they will still work” to properly separate independent clauses.
These small adjustments will improve the readability of the documentation.🧰 Tools
🪛 LanguageTool
[uncategorized] ~75-~75: You might be missing the article “the” here.
Context: ...nder function causes an error. The testdoesn't render html string returned directly from render function
demonstrates this. 2. **Non-...(AI_EN_LECTOR_MISSING_DETERMINER_THE)
[uncategorized] ~79-~79: You might be missing the article “a” here.
Context: ...nt Behavior** - Interestingly, the testreturns the object returned by async render function even if it doesn't have renderedHtml property
shows that Promise-returning ...(AI_EN_LECTOR_MISSING_DETERMINER_A)
[uncategorized] ~79-~79: Use a comma before ‘and’ if it connects two independent clauses (unless they are closely connected and short).
Context: ...bjects without arenderedHtml
property and they will still work. ## Ruby Helper F...(COMMA_COMPOUND_SENTENCE)
101-101
: Remove Trailing Punctuation from Heading
The heading#### When to use:
ends with a colon. For consistency with common Markdown style guidelines (e.g. MD026), consider removing the trailing punctuation.🧰 Tools
🪛 markdownlint-cli2 (0.17.2)
101-101: Trailing punctuation in heading
Punctuation: ':'(MD026, no-trailing-punctuation)
107-107
: Remove Trailing Punctuation from Heading
Similarly, the heading#### Not suitable for:
contains a trailing colon. Removing it will align with style guidelines.🧰 Tools
🪛 markdownlint-cli2 (0.17.2)
107-107: Trailing punctuation in heading
Punctuation: ':'(MD026, no-trailing-punctuation)
133-133
: Remove Trailing Punctuation from Heading
Again, the heading#### When to use:
in the react_component_hash section should drop the trailing colon for consistency.🧰 Tools
🪛 markdownlint-cli2 (0.17.2)
133-133: Trailing punctuation in heading
Punctuation: ':'(MD026, no-trailing-punctuation)
140-140
: Remove Trailing Punctuation from Heading
The heading#### Not suitable for:
in this section also has a trailing colon. Please remove it to conform with markdown style guidelines.🧰 Tools
🪛 markdownlint-cli2 (0.17.2)
140-140: Trailing punctuation in heading
Punctuation: ':'(MD026, no-trailing-punctuation)
145-145
: Remove Trailing Punctuation from Heading
The heading#### Requirements:
ends with a colon. For consistency, it is recommended to remove the trailing punctuation.🧰 Tools
🪛 markdownlint-cli2 (0.17.2)
145-145: Trailing punctuation in heading
Punctuation: ':'(MD026, no-trailing-punctuation)
223-233
: Clarify the Purpose of therenderFunction
Flag in Async Examples
In the asynchronous examples (Return Types 4 & 5), therenderFunction
flag is set to true. Consider adding a brief explanatory note (either inline or as a caption) clarifying that this flag distinguishes asynchronous render functions from synchronous ones. This will help readers understand its significance in the overall render process.
270-292
: Return Type 6: Redirect Object – Documentation Clarity
The redirect example is clear and the inline comments effectively communicate the intended behavior. For added clarity, you might consider a short summary sentence after the code block explaining that when a redirect condition is met, the helper renders nothing and instead performs the redirect logic.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (14)
docs/javascript/render-functions.md
(1 hunks)eslint.config.ts
(1 hunks)node_package/src/ReactOnRailsRSC.ts
(2 hunks)node_package/src/createReactOutput.ts
(2 hunks)node_package/src/handleError.ts
(1 hunks)node_package/src/isServerRenderResult.ts
(2 hunks)node_package/src/serverRenderReactComponent.ts
(6 hunks)node_package/src/serverRenderUtils.ts
(1 hunks)node_package/src/streamServerRenderedReactComponent.ts
(4 hunks)node_package/src/transformRSCStreamAndReplayConsoleLogs.ts
(2 hunks)node_package/src/types/index.ts
(4 hunks)node_package/tests/serverRenderReactComponent.test.js
(0 hunks)node_package/tests/serverRenderReactComponent.test.ts
(1 hunks)node_package/tests/streamServerRenderedReactComponent.test.jsx
(6 hunks)
💤 Files with no reviewable changes (1)
- node_package/tests/serverRenderReactComponent.test.js
🚧 Files skipped from review as they are similar to previous changes (10)
- node_package/src/handleError.ts
- eslint.config.ts
- node_package/src/transformRSCStreamAndReplayConsoleLogs.ts
- node_package/src/ReactOnRailsRSC.ts
- node_package/src/serverRenderUtils.ts
- node_package/src/createReactOutput.ts
- node_package/tests/serverRenderReactComponent.test.ts
- node_package/src/isServerRenderResult.ts
- node_package/src/streamServerRenderedReactComponent.ts
- node_package/src/types/index.ts
🧰 Additional context used
🪛 LanguageTool
docs/javascript/render-functions.md
[uncategorized] ~75-~75: You might be missing the article “the” here.
Context: ...nder function causes an error. The test doesn't render html string returned directly from render function
demonstrates this. 2. **Non-...
(AI_EN_LECTOR_MISSING_DETERMINER_THE)
[uncategorized] ~79-~79: You might be missing the article “a” here.
Context: ...nt Behavior** - Interestingly, the test returns the object returned by async render function even if it doesn't have renderedHtml property
shows that Promise-returning ...
(AI_EN_LECTOR_MISSING_DETERMINER_A)
[uncategorized] ~79-~79: Use a comma before ‘and’ if it connects two independent clauses (unless they are closely connected and short).
Context: ...bjects without a renderedHtml
property and they will still work. ## Ruby Helper F...
(COMMA_COMPOUND_SENTENCE)
[style] ~136-~136: You have already used this phrasing in nearby sentences. Consider replacing it to add variety to your writing.
Context: ...le HTML strings in an object - When you need to insert rendered content in different pa...
(REP_NEED_TO_VB)
[misspelling] ~147-~147: Did you mean “and”?
Context: ...nts: - The render function MUST return an object - The object MUST include a `com...
(AN_AND)
[uncategorized] ~155-~155: Loose punctuation mark.
Context: ...es errors - either use JSX or wrap in a { renderedHtml: '...' }
object. 3. **Async Functions Work wit...
(UNLIKELY_OPENING_PUNCTUATION)
🪛 markdownlint-cli2 (0.17.2)
docs/javascript/render-functions.md
101-101: Trailing punctuation in heading
Punctuation: ':'
(MD026, no-trailing-punctuation)
107-107: Trailing punctuation in heading
Punctuation: ':'
(MD026, no-trailing-punctuation)
133-133: Trailing punctuation in heading
Punctuation: ':'
(MD026, no-trailing-punctuation)
140-140: Trailing punctuation in heading
Punctuation: ':'
(MD026, no-trailing-punctuation)
145-145: Trailing punctuation in heading
Punctuation: ':'
(MD026, no-trailing-punctuation)
⏰ Context from checks skipped due to timeout of 90000ms (4)
- GitHub Check: dummy-app-integration-tests (oldest)
- GitHub Check: dummy-app-integration-tests (newest)
- GitHub Check: rspec-package-tests (oldest)
- GitHub Check: build
🔇 Additional comments (18)
node_package/tests/streamServerRenderedReactComponent.test.jsx (7)
63-97
: Well-structured enhancement to support multiple component types.The addition of the
componentType
parameter and the switch statement is a good approach to test different component rendering scenarios. This implementation covers all necessary test cases for the new async render function support.
118-118
: Good improvement: changedon
toonce
for 'end' event listeners.Changing from
on
toonce
for end event listeners prevents potential memory leaks and ensures the callback is executed exactly once when the stream ends.Also applies to: 137-137, 153-153, 169-169, 190-190
173-173
: Appropriate test assertion update.Changing from
toHaveLength
totoBeGreaterThanOrEqual
provides more flexibility in the tests, acknowledging that the exact number of chunks might vary but should meet a minimum threshold.Also applies to: 194-194
206-224
: Good test coverage for sync and async render functions.Using parameterized tests with
it.each
is an efficient way to test bothasyncRenderFunction
andrenderFunction
with the same expectations.
226-260
: Comprehensive error handling tests.These test cases properly verify that both synchronous and asynchronous errors are handled correctly in render functions.
262-307
: Good coverage for erroneous render functions.The tests correctly verify handling of errors thrown directly from render functions and the proper emission of those errors when
throwJsErrors
is true.
309-334
: Important test case for string promise resolution.This test is crucial as it verifies that the system can properly handle promises that resolve to strings, not just React elements, which expands the flexibility of the API.
node_package/src/serverRenderReactComponent.ts (6)
1-3
: Added React import for element validation.Good addition of the React import which enables the
isValidElement
check used later in the code. This makes the dependencies more explicit.
18-19
: New type imports support async React element handling.These new types support the async rendering functionality and are crucial for the type safety of the updated code.
48-57
: NewprocessReactElement
function with helpful error messages.This is a good addition that encapsulates the rendering of React elements with proper error handling. The detailed error message will help developers understand and fix issues related to render functions vs. React Function Components.
59-62
: Updated signature forprocessPromise
function.The function now accepts
CreateReactOutputAsyncResult
instead ofPromise<string>
and returnsRenderStateHtml
. This provides better type safety and flexibility.
71-76
: Enhanced promise handling to support React elements.The updated promise handling now checks if the resolved value is a valid React element and processes it accordingly. This is a key enhancement for supporting async render functions that return React elements.
However, this change might require updates to ReactOnRails Pro's NodeRenderer as noted in a previous review comment.
Please verify compatibility with ReactOnRails Pro's NodeRenderer, as this change in behavior might affect it.
85-87
:❓ Verification inconclusive
Handling of non-promise, non-server-render-hash results has changed.
Line 86 now directly calls
processReactElement
for non-promise, non-server-render-hash results, implying these results are expected to be React elements rather than strings. This is a significant behavioral change.Please verify that all existing code that uses this function now returns React elements instead of strings, or update the code to handle both cases.
🏁 Script executed:
#!/bin/bash # Search for places where components or render functions might return strings directly rg -A 3 "ComponentRegistry.register|registerComponent" --type=js --type=jsx --type=ts --type=tsxLength of output: 126
Action: Confirm React Element Consistency in
serverRenderReactComponent
UsageThe updated function now processes non-promise, non-server-render-hash results via
processReactElement
, which means callers are now expected to return React elements instead of strings. However, the initial verification command failed due to the unrecognized file type "jsx". To ensure that no callers still return strings, please run the following updated verification script that uses glob patterns to include JavaScript, JSX, TypeScript, and TSX files:#!/bin/bash # Search for occurrences of ComponentRegistry.register or registerComponent in various file types rg -g "*.js" -g "*.jsx" -g "*.ts" -g "*.tsx" -A 3 "ComponentRegistry\.register|registerComponent"After executing this command manually, confirm that all usage of the function now returns React elements. If any cases still return strings, update those call sites accordingly.
docs/javascript/render-functions.md (5)
7-10
: Example Section for React Components (JSX)
The first example clearly demonstrates a simple JSX return. The code snippet is clear and concise. No changes needed!
17-25
: Example Section for Objects withrenderedHtml
Property
The code example effectively illustrates returning an object with arenderedHtml
property. The formatting and clarity are good.
39-46
: Example Section for Promises of Strings
The async example is straightforward. The use ofasync
andawait
is correct and clear.
48-57
: Example Section for Promises of Objects
This snippet correctly shows how to return an object from an async function. The example is clear.
59-69
: Example Section for Redirect Information
The redirect example is precise and the inline comments clearly explain the purpose of each key. No modifications are necessary here.
7c4903d
to
77d7cea
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🧹 Nitpick comments (1)
docs/javascript/render-functions.md (1)
27-27
: Section title could be more descriptiveThe section title "Objects with Multiple HTML Properties" should better describe that these objects must include a
componentHtml
property.Consider renaming to "Objects with componentHtml Property (and Additional HTML Properties)" to match previous review feedback and better describe the content requirements.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (14)
docs/javascript/render-functions.md
(1 hunks)eslint.config.ts
(1 hunks)node_package/src/ReactOnRailsRSC.ts
(2 hunks)node_package/src/createReactOutput.ts
(2 hunks)node_package/src/handleError.ts
(1 hunks)node_package/src/isServerRenderResult.ts
(2 hunks)node_package/src/serverRenderReactComponent.ts
(6 hunks)node_package/src/serverRenderUtils.ts
(1 hunks)node_package/src/streamServerRenderedReactComponent.ts
(4 hunks)node_package/src/transformRSCStreamAndReplayConsoleLogs.ts
(2 hunks)node_package/src/types/index.ts
(4 hunks)node_package/tests/serverRenderReactComponent.test.js
(0 hunks)node_package/tests/serverRenderReactComponent.test.ts
(1 hunks)node_package/tests/streamServerRenderedReactComponent.test.jsx
(6 hunks)
💤 Files with no reviewable changes (1)
- node_package/tests/serverRenderReactComponent.test.js
🚧 Files skipped from review as they are similar to previous changes (9)
- node_package/src/transformRSCStreamAndReplayConsoleLogs.ts
- node_package/src/handleError.ts
- eslint.config.ts
- node_package/src/isServerRenderResult.ts
- node_package/src/serverRenderReactComponent.ts
- node_package/tests/serverRenderReactComponent.test.ts
- node_package/src/createReactOutput.ts
- node_package/src/streamServerRenderedReactComponent.ts
- node_package/src/serverRenderUtils.ts
🧰 Additional context used
🧬 Code Definitions (2)
node_package/src/ReactOnRailsRSC.ts (2)
node_package/src/types/index.ts (1)
RSCRenderParams
(156-158)node_package/src/loadReactClientManifest.ts (1)
loadReactClientManifest
(7-24)
node_package/tests/streamServerRenderedReactComponent.test.jsx (4)
lib/react_on_rails/react_component/render_options.rb (1)
props
(26-28)spec/dummy/client/app/store/composeInitialState.js (1)
props
(3-3)spec/dummy/client/app/startup/ReduxSharedStoreApp.client.jsx (1)
props
(16-50)node_package/src/ReactOnRails.client.ts (1)
streamServerRenderedReactComponent
(165-169)
🪛 LanguageTool
docs/javascript/render-functions.md
[uncategorized] ~75-~75: You might be missing the article “the” here.
Context: ...nder function causes an error. The test doesn't render html string returned directly from render function
demonstrates this. 2. **Non-...
(AI_EN_LECTOR_MISSING_DETERMINER_THE)
[uncategorized] ~79-~79: You might be missing the article “a” here.
Context: ...nt Behavior** - Interestingly, the test returns the object returned by async render function even if it doesn't have renderedHtml property
shows that Promise-returning ...
(AI_EN_LECTOR_MISSING_DETERMINER_A)
[uncategorized] ~79-~79: Use a comma before ‘and’ if it connects two independent clauses (unless they are closely connected and short).
Context: ...bjects without a renderedHtml
property and they will still work. ## Ruby Helper F...
(COMMA_COMPOUND_SENTENCE)
[style] ~136-~136: You have already used this phrasing in nearby sentences. Consider replacing it to add variety to your writing.
Context: ...le HTML strings in an object - When you need to insert rendered content in different pa...
(REP_NEED_TO_VB)
[misspelling] ~147-~147: Did you mean “and”?
Context: ...nts: - The render function MUST return an object - The object MUST include a `com...
(AN_AND)
[uncategorized] ~155-~155: Loose punctuation mark.
Context: ...es errors - either use JSX or wrap in a { renderedHtml: '...' }
object. 3. **Async Functions Work wit...
(UNLIKELY_OPENING_PUNCTUATION)
🪛 markdownlint-cli2 (0.17.2)
docs/javascript/render-functions.md
101-101: Trailing punctuation in heading
Punctuation: ':'
(MD026, no-trailing-punctuation)
107-107: Trailing punctuation in heading
Punctuation: ':'
(MD026, no-trailing-punctuation)
133-133: Trailing punctuation in heading
Punctuation: ':'
(MD026, no-trailing-punctuation)
140-140: Trailing punctuation in heading
Punctuation: ':'
(MD026, no-trailing-punctuation)
145-145: Trailing punctuation in heading
Punctuation: ':'
(MD026, no-trailing-punctuation)
⏰ Context from checks skipped due to timeout of 90000ms (2)
- GitHub Check: dummy-app-integration-tests (newest)
- GitHub Check: dummy-app-integration-tests (oldest)
🔇 Additional comments (28)
node_package/src/ReactOnRailsRSC.ts (2)
24-26
: Support added for async render function returnsThe function signature has been enhanced to accept a promise that resolves to either a React element or a string, which aligns with the PR objective of supporting async render functions.
37-38
: Good use of Promise.all for parallel executionUsing
Promise.all
allows concurrent resolution of both the React client manifest and the render function result, which is more efficient than sequential execution.docs/javascript/render-functions.md (7)
3-3
: Good addition of test file referenceIncluding the reference to the test file provides helpful context for understanding the documentation.
79-79
: Revise the wording about async function behaviorThe word "Interestingly" doesn't add value and the explanation could be clearer about why async functions don't require the
renderedHtml
property.Consider rewording to: "Async Functions Have Different Behavior - Promise-returning functions can return objects without a
renderedHtml
property, and they will still work because async functions are processed differently in the rendering pipeline."🧰 Tools
🪛 LanguageTool
[uncategorized] ~79-~79: You might be missing the article “a” here.
Context: ...nt Behavior** - Interestingly, the testreturns the object returned by async render function even if it doesn't have renderedHtml property
shows that Promise-returning ...(AI_EN_LECTOR_MISSING_DETERMINER_A)
[uncategorized] ~79-~79: Use a comma before ‘and’ if it connects two independent clauses (unless they are closely connected and short).
Context: ...bjects without arenderedHtml
property and they will still work. ## Ruby Helper F...(COMMA_COMPOUND_SENTENCE)
99-99
: Consider consolidating redundant sectionsThe examples of
react_component
with different return types all look the same in this section, which doesn't effectively illustrate the differences.Replace this section with a single statement explaining that
react_component
accepts render functions that return JSX, objects with arenderedHtml
property, and promises, then refer to the more detailed examples in the section below.
110-110
: Add examples of content types for different page partsBe more specific about what kind of content can be inserted in different parts of the page.
Add examples such as "meta tags, title tags, and style tags" to provide clearer guidance on what types of content can be distributed across the page.
114-114
: Add return type acceptance informationThis section should specify which render function return types it accepts, similar to the
react_component
section.Add a statement clarifying which return types are accepted by
react_component_hash
, such as "This helper accepts render functions that return objects with acomponentHtml
property and additional HTML properties."
151-151
: Remove reference to tests as authorityPhrases like "Based on the Tests" and "tests show" give the impression that tests are the authority rather than the implementation.
Revise the heading to "Best Practices" and remove references to tests as the source of authority.
157-157
: Redundant information with earlier sectionsThis section repeats information already covered in lines 73-79.
Consider removing or consolidating this redundant information to improve document flow and clarity.
node_package/tests/streamServerRenderedReactComponent.test.jsx (10)
63-64
: Good addition of componentType parameterAdding the
componentType
parameter tosetupStreamTest
allows for testing different component rendering scenarios, which is crucial for verifying the new async render function support.
65-97
: Well-structured component type handlingThe switch statement provides a clean way to register different types of components for testing, covering both synchronous and asynchronous scenarios, as well as error cases.
118-118
: Better event handling with onceChanging from
on('end', resolve)
toonce('end', resolve)
prevents potential memory leaks by ensuring the callback is only called once.
173-173
: More flexible test assertionsUsing
toBeGreaterThanOrEqual
instead oftoHaveLength
provides more flexibility when testing asynchronous operations that may produce a variable number of chunks.
206-224
: Comprehensive test coverage for different component typesThis parametrized test effectively verifies that both synchronous and asynchronous render functions can produce the same output when resolved properly.
226-240
: Good error handling testing for sync errorsThis test ensures that synchronous errors are properly caught and handled in both types of render functions.
242-260
: Good error handling testing for async errorsThis test ensures that asynchronous errors are properly caught and handled in both types of render functions.
262-282
: Comprehensive testing of erroneous component scenariosThis test ensures that errors occurring within the render functions themselves (rather than in the rendered components) are properly caught and handled.
284-307
: Complete error propagation testingThis test verifies that errors are properly propagated to the error handler when throwJsErrors is true, which is critical for proper error reporting.
309-334
: Test for string promise resolutionThis test verifies the ability to handle promises that resolve directly to strings, which is important for the new async render function support.
node_package/src/types/index.ts (9)
48-51
: Good addition of ServerRenderHashRenderedHtml typeThis type provides a clear structure for objects that contain rendered HTML, requiring a
componentHtml
property and allowing additional string properties.
54-54
: Improved type safety for renderedHtml propertyUsing the new
ServerRenderHashRenderedHtml
type for therenderedHtml
property provides better type safety and documentation.
60-64
: Enhanced type structure for CreateReactOutputResultBreaking down the result types into synchronous and asynchronous variants improves type clarity and allows for more precise type checking.
66-70
: Improved type structure for RenderFunctionResultSimilar to the CreateReactOutputResult changes, this breakdown into synchronous and asynchronous variants provides better type safety and documentation.
176-176
: Addition of FinalHtmlResult typeThis union type clearly defines what can be used as the final HTML result, improving type safety throughout the codebase.
179-179
: Updated RenderResult to use FinalHtmlResultThis change ensures consistency in the types used for HTML results and improves type safety.
186-188
: Addition of RSCPayloadChunk interfaceThis interface specifically constrains the html property to be a string, which is important for RSC payload chunks that must be stringified.
378-378
: Addition of RenderStateHtml typeThis type provides a clear definition for what can be used as the HTML in the render state, including both synchronous and asynchronous results.
381-381
: Updated RenderState to use RenderStateHtmlThis change ensures consistency in the types used for HTML results in the render state and improves type safety.
I renamed the file at the #1723 , it should be merged before merging this file |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull Request Overview
This PR aims to add support for async render function returns by updating various components in the test suite, server rendering logic, and type definitions, while also enhancing documentation.
- Updated tests to support additional component types and async error handling.
- Extended TypeScript types and modified streaming and server rendering functions to deal with Promise-based render function outputs.
- Added comprehensive documentation for render functions and their supported return types.
Reviewed Changes
Copilot reviewed 13 out of 13 changed files in this pull request and generated 1 comment.
Show a summary per file
File | Description |
---|---|
node_package/tests/streamServerRenderedReactComponent.test.jsx | Updates tests to use .once for stream events, add support for new component types, and revise expected error output formatting. |
node_package/tests/serverRenderReactComponent.test.ts | Refactors tests to use strict type assertions and updated render parameters for improved clarity with async and sync render functions. |
node_package/src/types/index.ts | Introduces new types to better support async render functions and server render hashes. |
node_package/src/streamServerRenderedReactComponent.ts | Updates streaming logic to handle Promise-based React components or string return values with improved error reporting. |
node_package/src/serverRenderUtils.ts, serverRenderReactComponent.ts, isServerRenderResult.ts, handleError.ts, createReactOutput.ts, ReactOnRailsRSC.ts | Refactors error handling, conversion functions and type processing to support the revised return types from render functions. |
eslint.config.ts | Updates allowed default projects to include additional test files. |
docs/javascript/render-functions.md | Adds a detailed usage guide on render functions, their return types, and proper usage with Rails helpers. |
Comments suppressed due to low confidence (1)
node_package/src/createReactOutput.ts:6
- [nitpick] The warning message logged when a render function returns a React Element directly is quite verbose; consider clarifying or simplifying the message and updating the documentation to clearly state that render functions should return a React component function rather than a JSX element.
function createReactElementFromRenderFunctionResult(renderFunctionResult: ReactComponent, name: string, props: Record<string, unknown> | undefined): React.ReactElement {
docs/javascript/render-functions.md
Outdated
|
||
2. **Objects Require Specific Properties** - Non-promise objects must include a `renderedHtml` property to be valid when used with `react_component`. | ||
|
||
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. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Probably redirectLocation
and routingError
should be described above (under "7. Redirect Information"?) and this should say something like
but it doesn't support redirect information [or "routing information"? and it should have a link to that] that can be returned by sync render function.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I already mentioned at the 7. Redirect Information section that
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.
I updated this content to have a link to the above section
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Personally, I don't believe the redirectLocation
property serves any purpose. It merely logs the location to which a redirection should occur, doesn’t it, @Judahmeek?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe that's right. Looks like a redirectLocation return is just to cut out any unnecessary processing.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should this PR also be updating spec/dummy to use the async render functions?
0306315
to
e467df4
Compare
ed16293
to
4b341f9
Compare
@Judahmeek We can only use promises in the node renderer, so I added an example to the dummy app of RORP. Please check it here |
Then the doc should say so, e.g. #1720 (comment). |
…verRenderReactComponent
…allback html at onShellError callback
…aming components and add more tests
…nd improve formatting
…d enhance documentation on render functions, clarifying parameter usage and return types.
…unctions" to "render-functions"
…o React on Rails Pro with the Node renderer
fe75977
to
86f8bfb
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🧹 Nitpick comments (4)
node_package/src/createReactOutput.ts (1)
5-23
: Good addition of helper function with clear warning message.The new
createReactElementFromRenderFunctionResult
function properly handles React components and elements, with a helpful warning message about the recommended patterns. This supports React Hooks compatibility by encouraging function components over direct JSX returns.Note that the warning indicates this will become an error in React on Rails v13, but we're beyond that version now. Consider updating the warning to reflect that this might cause issues with React Hooks currently.
-incorrectly returned a React Element (JSX). Instead, return a React Function Component by -wrapping your JSX in a function. ReactOnRails v13 will throw error on this, as React Hooks do not +incorrectly returned a React Element (JSX). Instead, return a React Function Component by +wrapping your JSX in a function. React Hooks do notdocs/javascript/render-functions.md (2)
38-122
: Comprehensive coverage of all return types with good examples.The documentation clearly explains the 7 different return types with practical code examples. The note about React components versus React elements is particularly important and will help users avoid common pitfalls.
Minor syntax improvement suggestion: Use more consistent formatting for the object examples. Some use ES6 arrow functions with JSX directly, while others use template literals. Consider standardizing the approach across examples.
🧰 Tools
🪛 LanguageTool
[uncategorized] ~63-~63: You might be missing the article “an” here.
Context: ... ### 3. Objects withrenderedHtml
as object containingcomponentHtml
and other pr...(AI_EN_LECTOR_MISSING_DETERMINER_AN)
134-197
: Clear guidance on helper methods choice with practical examples.The documentation clearly explains when to use each helper method (
react_component
vsreact_component_hash
) and provides examples for various scenarios. This will help users make the right choice based on their needs.Fix minor MarkdownLint warnings related to trailing punctuation in headings:
-#### When to use: +#### When to use -#### Not suitable for: +#### Not suitable for -#### Requirements: +#### Requirements🧰 Tools
🪛 LanguageTool
[misspelling] ~194-~194: Did you mean “and”?
Context: ...nts: - The render function MUST return an object - The object MUST include a `com...(AN_AND)
🪛 markdownlint-cli2 (0.17.2)
146-146: Trailing punctuation in heading
Punctuation: ':'(MD026, no-trailing-punctuation)
152-152: Trailing punctuation in heading
Punctuation: ':'(MD026, no-trailing-punctuation)
180-180: Trailing punctuation in heading
Punctuation: ':'(MD026, no-trailing-punctuation)
187-187: Trailing punctuation in heading
Punctuation: ':'(MD026, no-trailing-punctuation)
192-192: Trailing punctuation in heading
Punctuation: ':'(MD026, no-trailing-punctuation)
node_package/tests/streamServerRenderedReactComponent.test.jsx (1)
63-97
: Add type safety or an enum for component types
This switch statement correctly differentiates between multiple component types. However, consider creating a dedicated type or an enum (e.g.,'reactComponent' | 'renderFunction' | 'asyncRenderFunction'
etc.) to enforce type safety. This will help prevent typos and make future refactoring safer.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (14)
CHANGELOG.md
(1 hunks)docs/javascript/render-functions.md
(1 hunks)eslint.config.ts
(1 hunks)node_package/src/ReactOnRailsRSC.ts
(3 hunks)node_package/src/createReactOutput.ts
(2 hunks)node_package/src/handleError.ts
(1 hunks)node_package/src/isServerRenderResult.ts
(2 hunks)node_package/src/serverRenderReactComponent.ts
(6 hunks)node_package/src/serverRenderUtils.ts
(1 hunks)node_package/src/streamServerRenderedReactComponent.ts
(4 hunks)node_package/src/transformRSCStreamAndReplayConsoleLogs.ts
(2 hunks)node_package/src/types/index.ts
(5 hunks)node_package/tests/serverRenderReactComponent.test.ts
(1 hunks)node_package/tests/streamServerRenderedReactComponent.test.jsx
(6 hunks)
🚧 Files skipped from review as they are similar to previous changes (7)
- node_package/src/handleError.ts
- node_package/src/serverRenderUtils.ts
- eslint.config.ts
- node_package/src/transformRSCStreamAndReplayConsoleLogs.ts
- node_package/src/isServerRenderResult.ts
- node_package/src/streamServerRenderedReactComponent.ts
- node_package/src/ReactOnRailsRSC.ts
🧰 Additional context used
🧬 Code Definitions (2)
node_package/src/createReactOutput.ts (1)
node_package/src/isServerRenderResult.ts (2)
isServerRenderHash
(8-17)isPromise
(19-23)
node_package/tests/streamServerRenderedReactComponent.test.jsx (5)
spec/dummy/client/app/store/composeInitialState.js (1)
props
(3-3)spec/dummy/client/app/startup/ReduxSharedStoreApp.client.jsx (1)
props
(16-50)lib/react_on_rails/react_component/render_options.rb (1)
props
(26-28)spec/dummy/client/app/startup/CssModulesImagesFontsExample.jsx (1)
_props
(5-21)node_package/src/ReactOnRails.client.ts (1)
streamServerRenderedReactComponent
(165-169)
🪛 LanguageTool
docs/javascript/render-functions.md
[uncategorized] ~9-~9: Loose punctuation mark.
Context: ...nctions take two parameters: 1. props
: The props passed from the Ruby helper m...
(UNLIKELY_OPENING_PUNCTUATION)
[uncategorized] ~10-~10: Loose punctuation mark.
Context: ...le in your JavaScript. 2. railsContext
: Rails contextual information like curre...
(UNLIKELY_OPENING_PUNCTUATION)
[uncategorized] ~63-~63: You might be missing the article “an” here.
Context: ... ### 3. Objects with renderedHtml
as object containing componentHtml
and other pr...
(AI_EN_LECTOR_MISSING_DETERMINER_AN)
[uncategorized] ~128-~128: Loose punctuation mark.
Context: ...s an error. Always wrap HTML strings in { renderedHtml: '...' }
. 2. **Objects Require Specific Proper...
(UNLIKELY_OPENING_PUNCTUATION)
[misspelling] ~194-~194: Did you mean “and”?
Context: ...nts: - The render function MUST return an object - The object MUST include a `com...
(AN_AND)
🪛 markdownlint-cli2 (0.17.2)
docs/javascript/render-functions.md
146-146: Trailing punctuation in heading
Punctuation: ':'
(MD026, no-trailing-punctuation)
152-152: Trailing punctuation in heading
Punctuation: ':'
(MD026, no-trailing-punctuation)
180-180: Trailing punctuation in heading
Punctuation: ':'
(MD026, no-trailing-punctuation)
187-187: Trailing punctuation in heading
Punctuation: ':'
(MD026, no-trailing-punctuation)
192-192: Trailing punctuation in heading
Punctuation: ':'
(MD026, no-trailing-punctuation)
⏰ Context from checks skipped due to timeout of 90000ms (5)
- GitHub Check: dummy-app-integration-tests (newest)
- GitHub Check: dummy-app-integration-tests (oldest)
- GitHub Check: rspec-package-tests (oldest)
- GitHub Check: rspec-package-tests (newest)
- GitHub Check: build
🔇 Additional comments (40)
CHANGELOG.md (1)
30-31
: Feature addition looks good!Clear and concise documentation of the new feature, with proper attribution and linking to the PR.
node_package/src/createReactOutput.ts (3)
2-2
: Appropriately updated import to include necessary types.This change ensures the proper type definitions are available for the new functionality.
70-85
: Enhanced promise handling for async render functions.This change properly handles different types of results from promises, particularly supporting React components returned from async render functions. The
.then()
callback now checks if the result is a function and processes it accordingly.The code now correctly distinguishes between functions (React components) and other return types, applying the appropriate transformation in each case.
88-88
: Simplified function call using the extracted helper method.Good refactoring that uses the new helper function, making the code more maintainable and consistent.
docs/javascript/render-functions.md (4)
1-10
: Excellent introduction to render functions with clear explanations.The guide begins with a clear overview and properly explains the parameters that render functions receive. The link to the Rails Context documentation provides helpful context for users.
Consider adding a small introductory paragraph explaining the motivation for using render functions (data fetching, SSR benefits, etc.) to help users understand their value.
🧰 Tools
🪛 LanguageTool
[uncategorized] ~9-~9: Loose punctuation mark.
Context: ...nctions take two parameters: 1.props
: The props passed from the Ruby helper m...(UNLIKELY_OPENING_PUNCTUATION)
[uncategorized] ~10-~10: Loose punctuation mark.
Context: ...le in your JavaScript. 2.railsContext
: Rails contextual information like curre...(UNLIKELY_OPENING_PUNCTUATION)
12-36
: Well-documented methods for identifying render functions.The explanation of the two ways to mark a function as a render function is clear and provides good code examples. This is essential for users to understand how React on Rails identifies functions that should be treated as render functions versus regular React components.
124-132
: Valuable information about rendering behavior with reference to tests.Including the link to the test file and explaining important behaviors is excellent for helping developers understand the internal workings. This section provides crucial information about limitations and supported features.
🧰 Tools
🪛 LanguageTool
[uncategorized] ~128-~128: Loose punctuation mark.
Context: ...s an error. Always wrap HTML strings in{ renderedHtml: '...' }
. 2. **Objects Require Specific Proper...(UNLIKELY_OPENING_PUNCTUATION)
198-344
: Excellent comprehensive examples for all return types.The examples section ties everything together by showing how each return type works with the appropriate helper method. This practical guidance will significantly help developers implement these patterns correctly.
The format of showing both the JavaScript and Ruby code for each example is particularly helpful for understanding the end-to-end implementation.
node_package/src/serverRenderReactComponent.ts (4)
1-1
: Added necessary imports for React and new types.These additions support the enhanced functionality for handling React elements and async rendering.
Also applies to: 18-19
30-31
: Improved handling of rendered HTML results.The type inference for
htmlResult
and the use of the null coalescing operator (??
) improve the type safety and handle potential undefined values appropriately.Also applies to: 42-42, 45-45
48-57
: Added dedicated function for processing React elements with good error handling.The new
processReactElement
function properly renders React elements to strings and includes helpful error messaging for common mistakes with render functions. The error message clearly explains a potential cause (missing parameter for render functions) and how to fix it.
59-77
: Enhanced promise handling for async render functions.The updated
processPromise
function now handles different types of promise results, particularly supporting React components returned from async render functions. The function now checks if the resolved value is a valid React element and processes it accordingly.The error message clearly communicates that promises are only supported by the React on Rails Pro Node renderer, which is important for users to understand.
node_package/tests/streamServerRenderedReactComponent.test.jsx (9)
118-118
: Good use ofonce
instead ofon
Switching torenderResult.once('end', ...)
ensures the handler only fires once, which is appropriate for a singleend
event. This is a clean improvement.Also applies to: 137-137, 153-153, 169-169
173-173
: Relaxing expected chunk length
ReplacingtoHaveLength(2)
withtoBeGreaterThanOrEqual(2)
accommodates potential variations in streaming chunk boundaries. This change is consistent with streaming behavior.Also applies to: 194-194
179-179
: Refined error message pattern
Switching totoMatch(/the server rendering errored:\\n\\nAsync Error/)
ensures you're asserting the exact format of error output. This improves test specificity.Also applies to: 200-200
205-223
: Comprehensive test for resolved React component
Theit.each
approach for'asyncRenderFunction'
and'renderFunction'
effectively verifies that both synchronous and asynchronous render functions correctly produce streaming chunks. Great coverage.
226-240
: Expanding coverage of sync errors
These cases ensure sync errors in both async and standard render functions are handled. The thorough checks for chunk output greatly help catch regressions.
242-260
: Validating async errors
Testing async throw scenarios for both'asyncRenderFunction'
and'renderFunction'
ensures consistent error handling. This is necessary for robust streaming.
262-282
: Erroneous render function test
Verifying the'erroneousRenderFunction'
and'erroneousAsyncRenderFunction'
paths at the streaming layer is an excellent way to confirm your error capture logic.
284-307
: Differentiating error emission withthrowJsErrors: true
This set of tests confirms that an error event is emitted and properly tracked when JavaScript errors are configured to halt execution. Great scenario coverage.
309-334
: Testing a Promise-resolved string
Streaming a purely string-based result tests an important edge case. Your code ensures that a direct string is returned as proper HTML, which confirms broader promise-handling.node_package/tests/serverRenderReactComponent.test.ts (12)
4-10
: Using explicit type imports
ImportingRenderParams
,RenderResult
,RailsContext
, etc., clarifies test intentions and improves maintainability. Good practice to keep type usage explicit.
12-16
: Assert helper for string
assertIsString
is a clean utility to quickly narrow the type and throw if unexpected. It ensures strong runtime checks parallel your TypeScript declarations.
18-24
: Assert helper for promise
assertIsPromise
helps confirm the shape of asynchronous results. This approach reduces the chance of silent type mismatches.
32-51
: Initial test with typed render params
UsingRenderParams
in this test is a great improvement for clarity. The JSON parsing plusassertIsString
ensures your output is valid JSON and a valid string.
54-76
: Thorough error coverage
Catching “XYZ” in the rendered output verifies that runtime errors are surfaced. ConfirminghasErrors
is set to true is essential for diagnosing rendering issues.
78-101
: Handling customrenderedHtml
Your logic checks that a providedrenderedHtml
string is rendered unchanged. This scenario is beneficial for advanced server rendering or partial pre-render approaches.
103-124
: Graceful failure on invalid object
Confirming that returning an object without arenderedHtml
property triggers an exception is crucial. It prevents accidental misuse of the API.
135-158
: Skipping direct HTML string
The test ensures that a simple string like'<div>Hello</div>'
is treated as an invalid tag, forcing error handling. This clarifies that direct HTML should be used via arenderedHtml
property or a React element.
160-181
: Promise of a string
Verifying thatrenderingReturnsPromises: true
handles a stringly promise is beneficial. Your test ensures the final output is indeed that string, no matter the async path.
183-207
: Returning an object from async function
This scenario fosters better flexibility for specialized server rendering outputs. It's wise to confirm that the object is returned as-is.
209-231
: React component in async function
Ensuring the function returns a React component at runtime is valuable. This test verifies your ability to handle asynchronous component fetching.
251-279
: Rendering a renderer
Posting an error for attempts to render “a renderer” clarifies to users that they must differentiate between a component vs. a function that provides HTML or elements. Good error feedback.node_package/src/types/index.ts (7)
48-51
: StructuredServerRenderHashRenderedHtml
DefiningcomponentHtml
as mandatory and allowing additional string keys ensures flexible usage while retaining a clear minimal shape.
54-54
: ExtendingrenderedHtml
Mixingstring | ServerRenderHashRenderedHtml
expands possible rendering outputs. This is well-aligned with use cases that require more than just plain HTML.
60-71
: Refined type definitions for sync & async results
SeparatingCreateReactOutputSyncResult
fromCreateReactOutputAsyncResult
clarifies the differences between immediate and deferred outputs. These definitions strengthen your typed workflow.
117-122
: Additional exports
Adding these new exports in a coherent group helps keep your library consistent. This grouping makes it easier to see all related types in one place.
180-182
:FinalHtmlResult
inRenderResult
Updating thehtml
field toFinalHtmlResult | null
allows for plain strings orServerRenderHashRenderedHtml
variants. This is a flexible but safe approach.
189-191
:RSCPayloadChunk
extendsRenderResult
This interface nicely enforces a guaranteedhtml
string in payload chunks, distinguishing it from the possibly more openRenderResult.html
type.
381-385
:RenderStateHtml
for sync or async
AllowingRenderStateHtml
to be eitherFinalHtmlResult
orPromise<FinalHtmlResult>
captures the hybrid approach of your rendering pipeline. This well represents streaming or sync outcomes.
Enhanced Support for Async Render Functions Returning React Components
Summary
This PR enhances React on Rails to support async render functions that return React components. This completes the support for all major render function return types in both synchronous and asynchronous contexts.
Feature Details
Async render functions can now return React components directly, enabling a cleaner pattern for components that need to fetch data before rendering:
Then, render it in your Rails view:
Architectural Advantages
Instead of requiring developers to handle string rendering themselves (by calling
renderToString
inside their async functions), this approach provides significant flexibility and future-proofing:react_component
helper function or stream it by callingstream_react_component
without creating different implementations for each componentRelated Documentation
This feature completes the "Return Type 6: Promises of React Components" pattern described in our documentation.
Pull Request checklist
Remove this line after checking all the items here. If the item is not applicable to the PR, both check it out and wrap it by
~
.Add the CHANGELOG entry at the top of the file.
Other Information
Remove this paragraph and mention any other important and relevant information such as benchmarks.
This change is
Summary by CodeRabbit
Documentation
New Features
Bug Fixes