-
-
Notifications
You must be signed in to change notification settings - Fork 631
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 streaming rendered components using renderToPipeableStream
#1633
Add support for streaming rendered components using renderToPipeableStream
#1633
Conversation
WalkthroughThis update enhances the Changes
Possibly related PRs
Suggested reviewers
Poem
Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media? 🪧 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: 3
Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Files ignored due to path filters (2)
Gemfile.lock
is excluded by!**/*.lock
yarn.lock
is excluded by!**/yarn.lock
,!**/*.lock
Files selected for processing (9)
- lib/react_on_rails/helper.rb (5 hunks)
- lib/react_on_rails/react_component/render_options.rb (1 hunks)
- lib/react_on_rails/server_rendering_pool/ruby_embedded_java_script.rb (1 hunks)
- node_package/src/ReactOnRails.ts (2 hunks)
- node_package/src/serverRenderReactComponent.ts (4 hunks)
- node_package/src/types/index.ts (2 hunks)
- package.json (2 hunks)
- spec/dummy/config/webpack/alias.js (1 hunks)
- spec/dummy/config/webpack/webpackConfig.js (1 hunks)
Files skipped from review due to trivial changes (2)
- package.json
- spec/dummy/config/webpack/alias.js
Additional context used
Biome
node_package/src/types/index.ts
[error] 121-121: void is confusing inside a union type. (lint/suspicious/noConfusingVoidType)
Unsafe fix: Use undefined instead.
node_package/src/ReactOnRails.ts
[error] 110-110: Avoid the delete operator which can impact performance. (lint/performance/noDelete)
Unsafe fix: Use an undefined assignment instead.
[error] 117-117: Avoid the delete operator which can impact performance. (lint/performance/noDelete)
Unsafe fix: Use an undefined assignment instead.
[error] 185-185: The function should not return a value because its return type is void. (lint/correctness/noVoidTypeReturn)
The function is here:
'void' signals the absence of value. The returned value is likely to be ignored by the caller.
Additional comments not posted (6)
spec/dummy/config/webpack/webpackConfig.js (1)
7-7
: The fallback configuration for thestream
module usingstream-browserify
is correctly set up to ensure compatibility across environments.lib/react_on_rails/react_component/render_options.rb (1)
106-108
: Thestream?
method is appropriately added to check for the streaming option. This supports the new feature of server-side streaming rendering.node_package/src/types/index.ts (2)
2-2
: ImportingPassThrough
fromstream
is correctly added to support the types used in the new streaming functions.
141-141
: The addition ofstreamServerRenderedReactComponent
to theReactOnRails
interface correctly extends its capabilities to support streaming.node_package/src/serverRenderReactComponent.ts (1)
74-99
: The implementation of streaming usingReactDOMServer.renderToPipeableStream
and handling the stream withPassThrough
is correctly done. This is a crucial part of adding streaming support to the server rendering process.Also applies to: 209-249
lib/react_on_rails/helper.rb (1)
94-102
: Proper implementation of streaming support instream_react_component
.The method correctly sets up options for streaming and builds the result for server-streamed content. This is a crucial addition for supporting modern React streaming capabilities.
if (throwJsErrors) { | ||
throw e; | ||
} | ||
|
||
renderResult = stringToStream(handleError({ | ||
e, | ||
name, | ||
serverSide: true, | ||
})); |
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.
Consider enhancing error handling by adding more specific error messages or logging for different types of errors, especially in streaming contexts.
// Example: Add more specific error handling
if (e instanceof SpecificError) {
console.error(`Specific error occurred: ${e.message}`);
} else {
console.error(`General error in streaming: ${e}`);
}
node_package/src/ReactOnRails.ts
Outdated
@@ -1,10 +1,11 @@ | |||
import type { ReactElement } from 'react'; | |||
import type { PassThrough } from 'stream'; |
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.
Ensure correct implementation of streaming in streamServerRenderedReactComponent
.
The implementation of streamServerRenderedReactComponent
seems to recursively call itself, which will lead to a stack overflow error. This should be corrected to call the appropriate function that handles the streaming logic.
- return streamServerRenderedReactComponent(options);
+ return someInternalFunctionThatHandlesStreaming(options);
Also applies to: 8-8, 245-252
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
Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Files selected for processing (1)
- lib/react_on_rails/helper.rb (4 hunks)
Files skipped from review as they are similar to previous changes (1)
- lib/react_on_rails/helper.rb
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: 2
Outside diff range, codebase verification and nitpick comments (1)
node_package/src/serverRenderReactComponent.ts (1)
171-176
: Consider adding a brief explanation forPassThrough
.Adding a comment explaining the usage of
PassThrough
can improve readability for future maintainers.const stringToStream = (str: string) => { // PassThrough is a type of stream that passes data through without transforming it. const stream = new PassThrough(); stream.push(str); stream.push(null); return stream; };
Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Files selected for processing (1)
- node_package/src/serverRenderReactComponent.ts (2 hunks)
Additional comments not posted (1)
node_package/src/serverRenderReactComponent.ts (1)
178-220
: LGTM! But verify the function usage in the codebase.The code changes are approved.
However, ensure that all function calls to
streamServerRenderedReactComponent
are correctly implemented.Verification successful
Function usage verified.
The function
streamServerRenderedReactComponent
is correctly implemented in the codebase.
node_package/src/ReactOnRails.ts
: The methodstreamServerRenderedReactComponent
correctly calls the imported function with the appropriate parameters.Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash # Description: Verify all function calls to `streamServerRenderedReactComponent` are correctly implemented. # Test: Search for the function usage. Expect: Only occurances of the new function. rg --type typescript -A 5 $'streamServerRenderedReactComponent'Length of output: 97
Script:
#!/bin/bash # Description: Verify all function calls to `streamServerRenderedReactComponent` are correctly implemented. # Test: Search for the function usage across all files. rg -A 5 'streamServerRenderedReactComponent'Length of output: 2000
Script:
#!/bin/bash # Description: Verify the context around the function usage in `node_package/src/ReactOnRails.ts`. # Test: Display lines around the function usage in `node_package/src/ReactOnRails.ts`. rg -A 10 -B 10 'streamServerRenderedReactComponent' node_package/src/ReactOnRails.tsLength of output: 1322
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, codebase verification and nitpick comments (1)
lib/react_on_rails/helper.rb (1)
104-122
: Functionstream_react_component_async
is not used in the codebase.The method
stream_react_component_async
is defined but not called anywhere in the repository. Ensure that this function is integrated where necessary or remove it if it is not needed.
- File:
lib/react_on_rails/helper.rb
- Line: 104
Analysis chain
LGTM! But verify the function usage in the codebase.
The code changes are approved.
However, ensure that all function calls to
stream_react_component_async
are correctly implemented.Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash # Description: Verify all function calls to `stream_react_component_async` are correctly implemented. # Test: Search for the function usage. Expect: Only occurances of the new signature. rg --type ruby -A 5 $'stream_react_component_async'Length of output: 470
Script:
#!/bin/bash # Description: Verify all function calls to `stream_react_component_async` are correctly implemented. # Test: Search for the function usage across the entire codebase. rg 'stream_react_component_async'Length of output: 129
Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Files selected for processing (1)
- lib/react_on_rails/helper.rb (4 hunks)
Additional comments not posted (2)
lib/react_on_rails/helper.rb (2)
394-419
: LGTM! But verify the function usage in the codebase.The code changes are approved.
However, ensure that all function calls to
build_react_component_result_for_server_streamed_content
are correctly implemented.Verification successful
Function usage verified!
The function
build_react_component_result_for_server_streamed_content
is correctly implemented and used within the codebase.
- The method definition and its single call are both located in
lib/react_on_rails/helper.rb
.Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash # Description: Verify all function calls to `build_react_component_result_for_server_streamed_content` are correctly implemented. # Test: Search for the function usage. Expect: Only occurances of the new signature. rg --type ruby -A 5 $'build_react_component_result_for_server_streamed_content'Length of output: 985
Script:
#!/bin/bash # Description: Verify all function calls to `build_react_component_result_for_server_streamed_content` are correctly implemented across the entire codebase. # Test: Search for the function usage in the entire codebase. Expect: Only occurrences of the new signature. rg --type ruby 'build_react_component_result_for_server_streamed_content'Length of output: 261
94-102
: LGTM! But verify the function usage in the codebase.The code changes are approved.
However, ensure that all function calls to
stream_react_component
are correctly implemented.Verification successful
The search results indicate that the
stream_react_component
method is used within thestream_react_component_async
method. This usage appears to be correct. However, to be thorough, we need to ensure there are no other occurrences ofstream_react_component
in the codebase that could be impacted by the new changes.
Verified: The
stream_react_component
method is defined and used only within thelib/react_on_rails/helper.rb
file, specifically within thestream_react_component_async
method. No other occurrences were found in the codebase.
Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash # Description: Verify all function calls to `stream_react_component` are correctly implemented. # Test: Search for the function usage. Expect: Only occurances of the new signature. rg --type ruby -A 5 $'stream_react_component'Length of output: 1066
Script:
#!/bin/bash # Search for all occurrences of `stream_react_component` in the codebase to ensure correct implementation. rg 'stream_react_component'Length of output: 307
00c9552
to
fbb1060
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
Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Files selected for processing (2)
- .gitignore (1 hunks)
- lib/react_on_rails/helper.rb (4 hunks)
Files skipped from review due to trivial changes (1)
- .gitignore
Files skipped from review as they are similar to previous changes (1)
- lib/react_on_rails/helper.rb
fbb1060
to
7bb828e
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: 2
Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Files ignored due to path filters (1)
yarn.lock
is excluded by!**/yarn.lock
,!**/*.lock
Files selected for processing (10)
- .gitignore (1 hunks)
- lib/react_on_rails/helper.rb (4 hunks)
- lib/react_on_rails/react_component/render_options.rb (1 hunks)
- lib/react_on_rails/server_rendering_pool/ruby_embedded_java_script.rb (1 hunks)
- node_package/src/ReactOnRails.ts (2 hunks)
- node_package/src/serverRenderReactComponent.ts (2 hunks)
- node_package/src/types/index.ts (2 hunks)
- package.json (2 hunks)
- spec/dummy/config/webpack/alias.js (1 hunks)
- spec/dummy/config/webpack/webpackConfig.js (1 hunks)
Files skipped from review as they are similar to previous changes (2)
- .gitignore
- node_package/src/serverRenderReactComponent.ts
Additional comments not posted (10)
spec/dummy/config/webpack/alias.js (1)
7-7
: Addition of alias forstream
tostream-browserify
.This addition correctly maps the
stream
module tostream-browserify
, ensuring compatibility with browser environments. Ensure that this change does not conflict with other aliases or module resolutions in your project.spec/dummy/config/webpack/webpackConfig.js (1)
7-7
: Addition of fallback forstream
tostream-browserify
.This addition correctly sets a fallback for the
stream
module to usestream-browserify
, ensuring compatibility with browser environments. Ensure that this change does not conflict with other fallbacks or module resolutions in your project.package.json (2)
18-19
: Upgrade@types/react
and@types/react-dom
to18.2.0
.These upgrades align the type definitions with React 18, ensuring compatibility with the latest features and types. Verify that the rest of the codebase is compatible with these updates.
42-43
: Upgradereact
andreact-dom
to18.2.0
.These upgrades bring in the latest features and performance improvements of React 18. Ensure that the rest of the codebase is compatible with these updates.
lib/react_on_rails/react_component/render_options.rb (1)
106-108
: LGTM!The
stream?
method correctly retrieves the value associated with the:stream?
key from theoptions
hash.node_package/src/types/index.ts (2)
2-2
: LGTM!The import statement for
PassThrough
from thestream
module is correct and necessary for the new method.
141-141
: LGTM!The method
streamServerRenderedReactComponent
correctly returns aPassThrough
stream and enhances the functionality of theReactOnRails
interface.lib/react_on_rails/server_rendering_pool/ruby_embedded_java_script.rb (1)
96-99
: LGTM! But remember to merge withexec_server_render_js
.The method
exec_server_render_streaming_js
correctly invokes theeval_streaming_js
method. The TODO comment indicates an intention to merge this method withexec_server_render_js
in the future.lib/react_on_rails/helper.rb (2)
94-102
: LGTM!The
stream_react_component
function correctly merges options with astream?
flag and callsinternal_react_component
. The function also correctly invokesbuild_react_component_result_for_server_streamed_content
.
104-122
: LGTM!The
stream_react_component_async
function correctly uses Ruby'sFiber
to manage asynchronous streaming of React components. The function correctly creates a fiber that streams chunks of the component's output and yields the initial chunk.
node_package/src/ReactOnRails.ts
Outdated
streamServerRenderedReactComponent(options: RenderParams): PassThrough { | ||
return streamServerRenderedReactComponent(options); | ||
}, |
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.
Ensure correct implementation of streaming in streamServerRenderedReactComponent
.
The implementation of streamServerRenderedReactComponent
seems to recursively call itself, which will lead to a stack overflow error. This should be corrected to call the appropriate function that handles the streaming logic.
- return streamServerRenderedReactComponent(options);
+ return someInternalFunctionThatHandlesStreaming(options);
Committable suggestion was skipped due to low confidence.
lib/react_on_rails/helper.rb
Outdated
def build_react_component_result_for_server_streamed_content( | ||
rendered_html_stream: required("rendered_html_stream"), | ||
component_specification_tag: required("component_specification_tag"), | ||
render_options: required("render_options") | ||
) | ||
content_tag_options_html_tag = render_options.html_options[:tag] || "div" | ||
# The component_specification_tag is appended to the first chunk | ||
# We need to pass it early with the first chunk because it's needed in hydration | ||
# We need to make sure that client can hydrate the app early even before all components are streamed | ||
is_first_chunk = true | ||
rendered_html_stream = rendered_html_stream.transform do |chunk| | ||
if is_first_chunk | ||
is_first_chunk = false | ||
next <<-HTML | ||
#{rails_context_if_not_already_rendered} | ||
#{component_specification_tag} | ||
<#{content_tag_options_html_tag} id="#{render_options.dom_id}">#{chunk}</#{content_tag_options_html_tag}> | ||
HTML | ||
end | ||
chunk | ||
end | ||
|
||
rendered_html_stream.transform(&:html_safe) | ||
# TODO: handle console logs | ||
end |
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.
LGTM! But handle console logs.
The build_react_component_result_for_server_streamed_content
function correctly handles the first chunk of the stream and ensures the client can hydrate the app early. The function also correctly marks the content as html_safe
.
However, the TODO comment indicates that handling console logs is pending.
Do you want me to help with handling console logs or open a GitHub issue to track this task?
lib/react_on_rails/helper.rb
Outdated
@@ -91,6 +91,36 @@ def react_component(component_name, options = {}) | |||
end | |||
end | |||
|
|||
def stream_react_component(component_name, options = {}) | |||
options = options.merge(stream?: true) | |||
result = internal_react_component(component_name, options) |
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.
The result is of type StreamRequest
# We need to pass it early with the first chunk because it's needed in hydration | ||
# We need to make sure that client can hydrate the app early even before all components are streamed | ||
is_first_chunk = true | ||
rendered_html_stream = rendered_html_stream.transform do |chunk| |
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.
The StreamRequest and StreamDecorator classes allow us to transform the stream of http body chunks
@@ -39,8 +39,8 @@ | |||
"prettier": "^2.8.8", | |||
"prettier-eslint-cli": "^5.0.0", | |||
"prop-types": "^15.8.1", | |||
"react": "^17.0.0", | |||
"react-dom": "^17.0.0", | |||
"react": "^18.2.0", |
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.
renderToReadableStream
function is added in React 18
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.
Self-reviewed
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: 2
Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Files selected for processing (4)
- jest.config.js (1 hunks)
- node_package/src/serverRenderReactComponent.ts (2 hunks)
- node_package/tests/ReactOnRails.test.js (1 hunks)
- node_package/tests/jest.setup.js (1 hunks)
Additional comments not posted (5)
jest.config.js (1)
4-4
: Configuration Addition: setupFilesThe addition of the
setupFiles
property to the Jest configuration is appropriate. This allows for the initialization of necessary settings before each test suite runs.node_package/tests/jest.setup.js (1)
1-13
: Polyfills for TextEncoder and TextDecoderThe polyfills for
TextEncoder
andTextDecoder
are correctly implemented. The check to avoid redefining these globals if they are already defined is a good practice.node_package/tests/ReactOnRails.test.js (1)
22-30
: Improved Test Setup and VerificationThe changes to programmatically create the
<div>
element and set its ID and text content improve the clarity and accuracy of the test setup. This approach is more explicit and ensures the DOM is correctly set up before rendering the component.node_package/src/serverRenderReactComponent.ts (2)
170-175
: LGTM!The
stringToStream
function correctly converts a string to a stream usingPassThrough
.
177-220
: LGTM!The
streamServerRenderedReactComponent
function correctly implements the streaming of rendered React components usingrenderToPipeableStream
.
export const streamServerRenderedReactComponent = (options: RenderParams) => { | ||
const { name, domNodeId, trace, props, railsContext, throwJsErrors } = options; | ||
|
||
let renderResult: null | PassThrough = null; | ||
|
||
try { | ||
const componentObj = ComponentRegistry.get(name); | ||
if (componentObj.isRenderer) { | ||
throw new Error(`\ | ||
Detected a renderer while server rendering component '${name}'. \ | ||
See https://github.com/shakacode/react_on_rails#renderer-functions`); | ||
} | ||
|
||
const reactRenderingResult = createReactOutput({ | ||
componentObj, | ||
domNodeId, | ||
trace, | ||
props, | ||
railsContext, | ||
}); | ||
|
||
if (isServerRenderHash(reactRenderingResult) || isPromise(reactRenderingResult)) { | ||
throw new Error('Server rendering of streams is not supported for server render hashes or promises.'); | ||
} | ||
|
||
renderResult = new PassThrough(); | ||
ReactDOMServer.renderToPipeableStream(reactRenderingResult as ReactElement).pipe(renderResult); | ||
|
||
// TODO: Add console replay script to the stream | ||
// Ensure to avoid console messages leaking between different components rendering | ||
} catch (e: any) { | ||
if (throwJsErrors) { | ||
throw e; | ||
} | ||
|
||
renderResult = stringToStream(handleError({ | ||
e, | ||
name, | ||
serverSide: true, | ||
})); | ||
} | ||
|
||
return renderResult; | ||
}; |
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.
Enhance error handling with specific error messages.
Consider adding more specific error handling or logging for different types of errors, especially in streaming contexts.
// Example: Add more specific error handling
if (e instanceof SpecificError) {
console.error(`Specific error occurred: ${e.message}`);
} else {
console.error(`General error in streaming: ${e}`);
}
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, codebase verification and nitpick comments (1)
lib/react_on_rails/helper.rb (1)
104-124
: Issues Found:The function
stream_react_component_async
is defined but not invoked anywhere in the codebase. Additionally, there is no initialization found for@rorp_rendering_fibers
. This could lead to runtime errors and incomplete functionality.
- Ensure that
stream_react_component_async
is called appropriately where needed.- Verify that
@rorp_rendering_fibers
is properly initialized before it is used.Analysis chain
LGTM! But verify the function usage in the codebase.
The code changes are approved.
However, ensure that all function calls to
stream_react_component_async
are correctly implemented and@rorp_rendering_fibers
is properly initialized.Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash # Description: Verify all function calls to `stream_react_component_async` in the codebase. # Test: Search for the function usage. Expect: Correct implementation of the function calls. rg --type ruby -A 5 $'stream_react_component_async'Length of output: 470
Script:
#!/bin/bash # Description: Verify the usage and initialization of `stream_react_component_async` and `@rorp_rendering_fibers` in the codebase. # Search for all invocations of `stream_react_component_async` in the codebase. rg --type ruby 'stream_react_component_async' # Search for the initialization of `@rorp_rendering_fibers` in the codebase. rg --type ruby '@rorp_rendering_fibers\s*='Length of output: 187
Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Files selected for processing (2)
- lib/react_on_rails/helper.rb (4 hunks)
- spec/dummy/config/webpack/alias.js (1 hunks)
Files skipped from review as they are similar to previous changes (1)
- spec/dummy/config/webpack/alias.js
Additional comments not posted (2)
lib/react_on_rails/helper.rb (2)
396-420
: LGTM! But handle console logs.The
build_react_component_result_for_server_streamed_content
function correctly handles the first chunk of the stream and ensures the client can hydrate the app early. The function also correctly marks the content ashtml_safe
.However, the TODO comment indicates that handling console logs is pending.
Do you want me to help with handling console logs or open a GitHub issue to track this task?
94-102
: LGTM! But verify the function usage in the codebase.The code changes are approved.
However, ensure that all function calls to
stream_react_component
are correctly implemented.Verification successful
The search results indicate that
stream_react_component
is used within thestream_react_component_async
method in the same file. To ensure thorough verification, we need to check if there are any other usages ofstream_react_component
in the entire codebase.
Verified: No issues found with the usage of
stream_react_component
.The function
stream_react_component
is only used within thestream_react_component_async
method in the same file, and there are no other usages in the codebase.
Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash # Description: Verify all function calls to `stream_react_component` in the codebase. # Test: Search for the function usage. Expect: Correct implementation of the function calls. rg --type ruby -A 5 $'stream_react_component'Length of output: 1066
Script:
#!/bin/bash # Description: Verify all function calls to `stream_react_component` in the entire codebase. # Test: Search for the function usage in the entire codebase. Expect: Correct implementation of the function calls. rg 'stream_react_component'Length of output: 307
9ed4ce3
to
0c9955a
Compare
@@ -19,10 +19,15 @@ describe('ReactOnRails', () => { | |||
}); | |||
ReactOnRails.register({ R1 }); | |||
|
|||
document.body.innerHTML = '<div id="root"></div>'; | |||
// eslint-disable-next-line no-underscore-dangle | |||
const actual = ReactOnRails.render('R1', {}, 'root')._reactInternals.type; |
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.
When we use a newer react version that supports the Root API
, it returns the ReactDomRoot
object, which doesn't have the _reactInternals
property.
The |
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
Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Files selected for processing (3)
- lib/react_on_rails/helper.rb (5 hunks)
- node_package/src/serverRenderReactComponent.ts (2 hunks)
- spec/dummy/config/webpack/commonWebpackConfig.js (1 hunks)
Additional comments not posted (5)
spec/dummy/config/webpack/commonWebpackConfig.js (1)
44-44
: LGTM! The change enhances compatibility.The addition of
process: 'process/browser'
in theProvidePlugin
ensures that theprocess
object is available in the browser environment, improving compatibility with libraries that rely on Node.js globals.node_package/src/serverRenderReactComponent.ts (2)
170-175
: LGTM! The function correctly converts a string to a stream.The
stringToStream
function usesPassThrough
to create a stream from a string, which is a correct and efficient approach.
177-220
: LGTM! But verify the error handling mechanism.The
streamServerRenderedReactComponent
function correctly handles server-side rendering through streaming usingrenderToPipeableStream
. The error handling mechanism seems robust, but ensure that all possible errors are correctly managed.Verification successful
LGTM! The error handling mechanism in
streamServerRenderedReactComponent
is robust.The function correctly handles errors by catching exceptions, checking the
throwJsErrors
flag, and either rethrowing the error or converting it to a stream usinghandleError
. This approach is consistent with the rest of the codebase.
node_package/src/serverRenderReactComponent.ts
: Lines 177-220Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash # Description: Verify the error handling mechanism in `streamServerRenderedReactComponent`. # Test: Search for all error handling code. Expect: Proper error handling and logging. rg --type typescript 'catch' -A 5Length of output: 68
Script:
#!/bin/bash # Description: Verify the error handling mechanism in `streamServerRenderedReactComponent`. # Test: Search for all error handling code. Expect: Proper error handling and logging. rg --type ts 'catch' -A 5Length of output: 3753
lib/react_on_rails/helper.rb (2)
355-363
: LGTM! The method correctly handles the internal streaming logic.The
stream_react_component_internal
method merges options with astream?
flag and correctly invokes an internal method to generate the component's rendered HTML.
94-114
: LGTM! But verify the error handling mechanism.The
stream_react_component
method correctly uses Ruby'sFiber
to manage asynchronous streaming of React components. The error handling mechanism seems robust, but ensure that all possible errors are correctly managed.Verification successful
Error Handling Verified
The
stream_react_component
method's error handling mechanism is robust and consistent with the rest of the codebase. The use ofraise
for error handling is appropriately implemented.
lib/react_on_rails/helper.rb
lines 94-114: Error handling is verified.Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash # Description: Verify the error handling mechanism in `stream_react_component`. # Test: Search for all error handling code. Expect: Proper error handling and logging. rg --type ruby 'raise' -A 5Length of output: 65682
lib/react_on_rails/helper.rb
Outdated
def build_react_component_result_for_server_streamed_content( | ||
rendered_html_stream: required("rendered_html_stream"), | ||
component_specification_tag: required("component_specification_tag"), | ||
render_options: required("render_options") | ||
) | ||
content_tag_options_html_tag = render_options.html_options[:tag] || "div" | ||
# The component_specification_tag is appended to the first chunk | ||
# We need to pass it early with the first chunk because it's needed in hydration | ||
# We need to make sure that client can hydrate the app early even before all components are streamed | ||
is_first_chunk = true | ||
rendered_html_stream = rendered_html_stream.transform do |chunk| | ||
if is_first_chunk | ||
is_first_chunk = false | ||
html_content = <<-HTML | ||
#{rails_context_if_not_already_rendered} | ||
#{component_specification_tag} | ||
<#{content_tag_options_html_tag} id="#{render_options.dom_id}">#{chunk}</#{content_tag_options_html_tag}> | ||
HTML | ||
html_content.strip | ||
end | ||
chunk | ||
end | ||
|
||
rendered_html_stream.transform(&:html_safe) | ||
# TODO: handle console logs | ||
end |
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.
LGTM! But handle the TODO comment.
The build_react_component_result_for_server_streamed_content
method correctly manages the specifics of formatting the streamed content. However, the TODO comment indicates that handling console logs is pending.
Do you want me to help with handling console logs or open a GitHub issue to track this task?
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
Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Files selected for processing (1)
- lib/react_on_rails/helper.rb (5 hunks)
Files skipped from review as they are similar to previous changes (1)
- lib/react_on_rails/helper.rb
d5d5d68
to
8937c5f
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: 2
🧹 Outside diff range and nitpick comments (12)
jest.config.js (1)
4-4
: LGTM! Consider adding a comment for clarity.The addition of the
setupFiles
configuration is a good practice for setting up the test environment. It's particularly useful for adding polyfills likeTextEncoder
andTextDecoder
that are not available in the default jsdom environment.Consider adding a brief comment explaining the purpose of this setup file:
module.exports = { preset: 'ts-jest/presets/js-with-ts', testEnvironment: 'jsdom', + // Setup file to add polyfills for TextEncoder and TextDecoder in the test environment setupFiles: ['<rootDir>/node_package/tests/jest.setup.js'], };
This comment would provide immediate context for other developers who might work on this configuration in the future.
spec/dummy/config/webpack/alias.js (1)
7-7
: Approve the stream alias addition with a suggestion for documentation.The addition of the
stream: 'stream-browserify'
alias is crucial for enabling the new streaming features in a browser environment. This change allows the use of Node.js-like streams in the browser, which is necessary for therenderToPipeableStream
functionality from React 18.Consider adding a comment above this line to explain the purpose of this alias, such as:
// Provide a browser-compatible implementation of Node.js streams for client-side rendering stream: 'stream-browserify',This will help other developers understand the reason for this alias in the future.
node_package/tests/jest.setup.js (3)
1-6
: Approve error checking with a minor suggestion.The error checking logic is well-implemented. It ensures that the polyfill is only applied when necessary and will fail fast if jsdom adds native support for TextEncoder.
Consider adding a more descriptive comment explaining why this check is important:
- // If jsdom environment is set and TextEncoder is not defined, then define TextEncoder and TextDecoder - // The current version of jsdom does not support TextEncoder and TextDecoder - // The following code will tell us when jsdom supports TextEncoder and TextDecoder + // Check if we're in a jsdom environment and if TextEncoder is already defined + // This helps us identify when jsdom adds native support for TextEncoder and TextDecoder + // allowing us to remove this polyfill when it's no longer needed
8-13
: Approve polyfill implementation with a minor suggestion for clarity.The polyfill implementation is correct and properly scoped to browser-like environments. It correctly imports the necessary classes from the 'util' module and assigns them to the global scope.
Consider adding a brief comment explaining why we're using require() inside an if statement:
if (typeof window !== 'undefined') { + // Dynamic import is used here to avoid loading 'util' in non-browser environments // eslint-disable-next-line global-require const { TextEncoder, TextDecoder } = require('util'); global.TextEncoder = TextEncoder; global.TextDecoder = TextDecoder; }
1-13
: Well-implemented polyfill for TextEncoder and TextDecoder in jsdom environments.This setup file effectively addresses the current limitation of jsdom not supporting TextEncoder and TextDecoder. The implementation is well-structured, with proper error checking and scoped application of the polyfill. It also includes a mechanism to easily identify when the polyfill is no longer needed, which is a good practice for maintaining code hygiene.
As the project evolves, consider the following:
- Regularly check for updates to jsdom that might include native support for TextEncoder and TextDecoder.
- If this polyfill is used in multiple places, consider creating a separate utility file for it to promote reusability and easier maintenance.
spec/dummy/config/webpack/webpackConfig.js (1)
Line range hint
32-33
: Consider removing or documenting the commented debugger statementThe commented-out debugger statement at the end of the file might be useful for development purposes. However, it's generally a good practice to remove such statements from production code or provide clear documentation if they're intentionally left for future debugging.
Consider either removing this line or adding a comment explaining its purpose and when it should be used.
spec/dummy/config/webpack/commonWebpackConfig.js (1)
44-44
: Approve: Addition of process polyfill for browser environmentThe addition of
process: 'process/browser'
to the webpack.ProvidePlugin configuration is a good solution for making theprocess
object available in the browser environment. This change is likely necessary for supporting React 18 features or related code that depends onprocess
.Consider adding a comment explaining why this polyfill is necessary, for better maintainability. For example:
new webpack.ProvidePlugin({ $: 'jquery', jQuery: 'jquery', // Polyfill for 'process' in browser environment, required for React 18 streaming features process: 'process/browser', }),package.json (1)
Line range hint
54-56
: Update peerDependencies to reflect new minimum versionsWhile the
devDependencies
have been updated to use React 18, thepeerDependencies
section still specifies ">= 16" for React and React DOM. Consider updating these to reflect the new minimum supported versions.Here's a suggested change:
"peerDependencies": { "js-yaml": ">= 3.0.0", - "react": ">= 16", - "react-dom": ">= 16" + "react": ">= 18", + "react-dom": ">= 18" },node_package/src/types/index.ts (2)
172-172
: LGTM: New method for streaming server-rendered componentsThe addition of the
streamServerRenderedReactComponent
method to theReactOnRails
interface is well-designed and aligns with the PR objectives. It maintains API consistency by using theRenderParams
type for its parameter, and theReadable
return type enables streaming of rendered components.Consider adding a JSDoc comment for this new method to provide more context on its usage and benefits. For example:
/** * Streams a server-rendered React component. * @param options - The rendering parameters. * @returns A readable stream of the rendered component. * @remarks This method is useful for large components or slow connections, as it allows for progressive rendering. */ streamServerRenderedReactComponent(options: RenderParams): Readable;
Line range hint
1-179
: Summary: Successful introduction of streaming functionalityThe changes to this file effectively introduce streaming capability for server-rendered React components, as outlined in the PR objectives. The new
streamServerRenderedReactComponent
method complements the existingserverRenderReactComponent
method, providing developers with a streaming alternative that can potentially improve performance for large components or slow connections.The additions are minimal, well-integrated, and consistent with the existing API design. They maintain type safety and extend the
ReactOnRails
interface logically.As this change introduces a new rendering method, consider updating any relevant documentation or guides to explain when and how to use the streaming method versus the standard rendering method. This will help developers make informed decisions about which method to use in different scenarios.
node_package/src/serverRenderReactComponent.ts (1)
196-201
: Maintain consistency in function declaration styleThe codebase uses both function declarations and function expressions. For consistency and clarity, consider using one style throughout the file.
Consider changing the
stringToStream
function to match the prevalent style in the file.If other functions are declared as function declarations, you can update
stringToStream
accordingly:-const stringToStream = (str: string): Readable => { +function stringToStream(str: string): Readable { // function body }Alternatively, if the preferred style is function expressions, consider updating other functions to match.
node_package/src/ReactOnRails.ts (1)
251-254
: Enhance method documentation with@returns
annotation.Consider adding an
@returns
tag to the JSDoc comment to describe the return value of thestreamServerRenderedReactComponent
method for improved clarity and maintainability.Apply this diff to add the
@returns
annotation:/** * Used by server rendering by Rails * @param options + * @returns Readable stream of the rendered component */
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
⛔ Files ignored due to path filters (1)
yarn.lock
is excluded by!**/yarn.lock
,!**/*.lock
📒 Files selected for processing (14)
- jest.config.js (1 hunks)
- lib/react_on_rails/helper.rb (5 hunks)
- lib/react_on_rails/react_component/render_options.rb (1 hunks)
- lib/react_on_rails/server_rendering_pool/ruby_embedded_java_script.rb (1 hunks)
- node_package/src/ReactOnRails.ts (2 hunks)
- node_package/src/serverRenderReactComponent.ts (2 hunks)
- node_package/src/types/index.ts (2 hunks)
- node_package/tests/ReactOnRails.test.js (1 hunks)
- node_package/tests/jest.setup.js (1 hunks)
- package.json (2 hunks)
- spec/dummy/config/webpack/alias.js (1 hunks)
- spec/dummy/config/webpack/commonWebpackConfig.js (1 hunks)
- spec/dummy/config/webpack/webpackConfig.js (1 hunks)
- spec/dummy/spec/helpers/react_on_rails_helper_spec.rb (2 hunks)
🧰 Additional context used
🔇 Additional comments (9)
spec/dummy/config/webpack/webpackConfig.js (1)
7-7
: LGTM: Fallback for 'stream' module added correctlyThe addition of the fallback for the 'stream' module using 'stream-browserify' is a good approach. This change ensures that the client-side bundle can work with stream-like functionality in browser environments, which is essential for the new streaming capabilities introduced in this PR.
This modification aligns well with the PR objectives of adding support for streaming rendered components and enhances the compatibility of the client-side code with the new server-side streaming features.
spec/dummy/config/webpack/commonWebpackConfig.js (1)
Line range hint
1-53
: Approve: Well-structured Webpack configurationThe overall structure and content of this Webpack configuration file are well-organized and appropriate for the project's needs. It effectively extends the base client configuration, adds necessary loaders for SCSS and jQuery, and includes the new
process
polyfill. The use ofmerge
for combining configurations is a clean and maintainable approach.package.json (1)
18-19
: Approval: React and React DOM version updatesThe updates to React, React DOM, and their type definitions from v17 to v18 are appropriate and align with the PR objectives of introducing React 18 features, particularly for server-side streaming capabilities.
Also applies to: 42-43
lib/react_on_rails/react_component/render_options.rb (1)
110-112
: 🛠️ Refactor suggestionConsider refactoring
stream?
method for consistency and flexibilityWhile the current implementation is simple and functional, it could be improved for consistency with other option retrieval methods in this class. Consider the following suggestions:
- Use the
retrieve_configuration_value_for
method to allow for a default value from the configuration:private def stream? retrieve_configuration_value_for(:stream?) end
- If there's no specific reason for this method to be public, consider making it private for better encapsulation.
These changes would align the
stream?
method with the class's existing patterns and provide more flexibility in handling default values.node_package/tests/ReactOnRails.test.js (1)
22-30
: Improved test robustness and compatibilityThe changes to this test case are well-implemented and address several important points:
- The test now focuses on the actual output (text content) rather than relying on internal React structures, making it more robust across different React versions.
- The new approach aligns well with the PR objectives of enhancing rendering capabilities, particularly for asynchronous components.
- This change effectively addresses the previous comment about newer React versions not having the
_reactInternals
property.The test is now more maintainable and less likely to break with future React updates.
node_package/src/types/index.ts (1)
2-2
: LGTM: New import forReadable
typeThe addition of the
Readable
type import from the 'stream' module is appropriate. This import is necessary for typing the return value of the newstreamServerRenderedReactComponent
method, which aligns with the PR objective of adding support for streaming rendered components.lib/react_on_rails/server_rendering_pool/ruby_embedded_java_script.rb (1)
95-99
: 🛠️ Refactor suggestionConsider refactoring and improving error handling for streaming render method.
The new
exec_server_render_streaming_js
method introduces streaming capabilities, which aligns with the PR objectives. However, there are a few points to consider:
As noted in the TODO comment, consider merging this method with
exec_server_render_js
to reduce duplication and maintain a single entry point for server rendering.Unlike
exec_server_render_js
, this method lacks error handling and tracing. Consider adding these features for consistency and robustness.The
eval_streaming_js
method is called onjs_evaluator
, but it's not clear where this method is defined. Ensure that all js_evaluators implement this method.Consider refactoring the method as follows:
def exec_server_render_js(js_code, render_options, js_evaluator = nil, streaming: false) js_evaluator ||= self if render_options.trace trace_js_code_used("Evaluating code to server render#{streaming ? ' (streaming)' : ''}.", js_code) end begin result = streaming ? js_evaluator.eval_streaming_js(js_code, render_options) : js_evaluator.eval_js(js_code, render_options) process_result(result, render_options) unless streaming rescue StandardError => err handle_error(err) end end def exec_server_render_streaming_js(js_code, render_options, js_evaluator = nil) exec_server_render_js(js_code, render_options, js_evaluator, streaming: true) endThis refactoring combines both methods, adds error handling and tracing for streaming, and maintains backwards compatibility.
node_package/src/ReactOnRails.ts (1)
2-2
: Importing 'Readable' stream type is appropriate.The addition of
import type { Readable } from 'stream';
correctly imports theReadable
type, which is used in the new streaming methodstreamServerRenderedReactComponent
.lib/react_on_rails/helper.rb (1)
94-152
: Well-implementedstream_react_component
methodThe
stream_react_component
method effectively introduces streaming capabilities using Fibers to manage server-side rendering with React 18 features like Suspense and concurrent rendering. The error handling and documentation are clear and appropriate.
describe "#rails_context_if_not_already_rendered" do | ||
let(:helper) { PlainReactOnRailsHelper.new } | ||
|
||
before do | ||
allow(helper).to receive(:rails_context).and_return({ some: "context" }) | ||
end | ||
|
||
it "returns a script tag with rails context when not already rendered" do | ||
result = helper.send(:rails_context_if_not_already_rendered) | ||
expect(result).to include('<script type="application/json" id="js-react-on-rails-context">') | ||
expect(result).to include('"some":"context"') | ||
end | ||
|
||
it "returns an empty string when already rendered" do | ||
helper.instance_variable_set(:@rendered_rails_context, true) | ||
result = helper.send(:rails_context_if_not_already_rendered) | ||
expect(result).to eq("") | ||
end | ||
|
||
it "calls rails_context with server_side: false" do | ||
helper.send(:rails_context_if_not_already_rendered) | ||
expect(helper).to have_received(:rails_context).with(server_side: false) | ||
end | ||
end |
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.
💡 Codebase verification
🛠️ Refactor suggestion
Thread Safety Issue Identified in rails_context_if_not_already_rendered
.
The current implementation of rails_context_if_not_already_rendered
uses a simple boolean (@rendered_rails_context
) to track rendering state. This approach is not thread-safe and could lead to race conditions in a multi-threaded environment.
Suggested Improvements:
-
Implement Thread Safety:
Use a thread-safe mechanism such asConcurrent::AtomicBoolean
to manage the@rendered_rails_context
state.def rails_context_if_not_already_rendered @rendered_rails_context ||= Concurrent::AtomicBoolean.new(false) return '' if @rendered_rails_context.true? context = rails_context(server_side: false) @rendered_rails_context.make_true content_tag(:script, context.to_json.html_safe, type: 'application/json', id: 'js-react-on-rails-context') end
-
Add Thread Safety Test:
Incorporate a test to verify that the method behaves correctly in a multi-threaded scenario.it "is thread-safe" do threads = Array.new(10) do Thread.new do helper.send(:rails_context_if_not_already_rendered) end end results = threads.map(&:value) expect(results.count(&:present?)).to eq(1) end
These changes will ensure that the rails_context_if_not_already_rendered
method operates safely in concurrent environments.
🔗 Analysis chain
Consider enhancing test coverage for rails_context_if_not_already_rendered
.
The new test block for rails_context_if_not_already_rendered
provides good coverage of the basic functionality. However, consider the following improvements:
- Add a test to verify that
@rendered_rails_context
is set to true after rendering. - Include a test for thread safety, as mentioned in the previous comment.
- Consider making the method public if it's intended to be part of the public API, or add a comment explaining why it's tested as a private method.
Here's an example of an additional test you could add:
it "sets @rendered_rails_context to true after rendering" do
expect {
helper.send(:rails_context_if_not_already_rendered)
}.to change { helper.instance_variable_get(:@rendered_rails_context) }.from(nil).to(true)
end
Additionally, to address the thread safety concern, consider updating the implementation as suggested in the previous comment:
def rails_context_if_not_already_rendered
@rendered_rails_context ||= Concurrent::AtomicBoolean.new(false)
return '' if @rendered_rails_context.true?
context = rails_context(server_side: false)
@rendered_rails_context.make_true
content_tag(:script, context.to_json.html_safe, type: 'application/json', id: 'js-react-on-rails-context')
end
Then, add a test to verify thread safety:
it "is thread-safe" do
threads = Array.new(10) do
Thread.new do
helper.send(:rails_context_if_not_already_rendered)
end
end
results = threads.map(&:value)
expect(results.count(&:present?)).to eq(1)
end
To ensure the rails_context_if_not_already_rendered
method is correctly implemented in the actual helper, let's verify its presence and implementation:
This will help us confirm that the method exists and review its current implementation.
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# Search for the rails_context_if_not_already_rendered method in the codebase
rg -A 10 'def rails_context_if_not_already_rendered'
Length of output: 727
const stringToStream = (str: string): Readable => { | ||
const stream = new PassThrough(); | ||
stream.push(str); | ||
stream.push(null); | ||
return stream; | ||
}; |
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.
🛠️ Refactor suggestion
Consider using Readable.from
for efficient stream creation
The stringToStream
function can be simplified by using Readable.from
instead of manually pushing data into a PassThrough
stream.
Here's how you can refactor the function:
-const stringToStream = (str: string): Readable => {
- const stream = new PassThrough();
- stream.push(str);
- stream.push(null);
- return stream;
-};
+const stringToStream = (str: string): Readable => {
+ return Readable.from([str]);
+};
This approach is more concise and leverages the built-in method for creating a stream from an iterable.
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
const stringToStream = (str: string): Readable => { | |
const stream = new PassThrough(); | |
stream.push(str); | |
stream.push(null); | |
return stream; | |
}; | |
const stringToStream = (str: string): Readable => { | |
return Readable.from([str]); | |
}; |
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: 3
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
📒 Files selected for processing (1)
- lib/react_on_rails/helper.rb (5 hunks)
🔇 Additional comments (3)
lib/react_on_rails/helper.rb (3)
391-399
: Previous review comment is still applicable.
502-513
: Previous review comment is still applicable.
515-517
: LGTM!The method correctly handles prepending the Rails context while maintaining proper string formatting.
# TODO: handle errors for streams | ||
return result if render_options.stream? | ||
|
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.
Implement error handling for streaming components.
The TODO comment indicates that error handling for streaming components is missing. This could lead to silent failures or unclear error messages in production.
Consider implementing error handling similar to the non-streaming case. Would you like me to help design the error handling mechanism for streaming components?
# Streams a server-side rendered React component using React's `renderToPipeableStream`. | ||
# Supports React 18 features like Suspense, concurrent rendering, and selective hydration. | ||
# Enables progressive rendering and improved performance for large components. | ||
# | ||
# Note: This function can only be used with React on Rails Pro. | ||
# The view that uses this function must be rendered using the | ||
# `stream_view_containing_react_components` method from the React on Rails Pro gem. | ||
# | ||
# Example of an async React component that can benefit from streaming: | ||
# | ||
# const AsyncComponent = async () => { | ||
# const data = await fetchData(); | ||
# return <div>{data}</div>; | ||
# }; | ||
# | ||
# function App() { | ||
# return ( | ||
# <Suspense fallback={<div>Loading...</div>}> | ||
# <AsyncComponent /> | ||
# </Suspense> | ||
# ); | ||
# } | ||
# | ||
# @param [String] component_name Name of your registered component | ||
# @param [Hash] options Options for rendering | ||
# @option options [Hash] :props Props to pass to the react component | ||
# @option options [String] :dom_id DOM ID of the component container | ||
# @option options [Hash] :html_options Options passed to content_tag | ||
# @option options [Boolean] :prerender Set to false to disable server-side rendering | ||
# @option options [Boolean] :trace Set to true to add extra debugging information to the HTML | ||
# @option options [Boolean] :raise_on_prerender_error Set to true to raise exceptions during server-side rendering | ||
# Any other options are passed to the content tag, including the id. | ||
def stream_react_component(component_name, options = {}) | ||
unless ReactOnRails::Utils.react_on_rails_pro? | ||
raise ReactOnRails::Error, | ||
"You must use React on Rails Pro to use the stream_react_component method." | ||
end | ||
|
||
if @rorp_rendering_fibers.nil? | ||
raise ReactOnRails::Error, | ||
"You must call stream_view_containing_react_components to render the view containing the react component" | ||
end | ||
|
||
rendering_fiber = Fiber.new do | ||
stream = internal_stream_react_component(component_name, options) | ||
stream.each_chunk do |chunk| | ||
Fiber.yield chunk | ||
end | ||
end | ||
|
||
@rorp_rendering_fibers << rendering_fiber | ||
|
||
# return the first chunk of the fiber | ||
# It contains the initial html of the component | ||
# all updates will be appended to the stream sent to browser | ||
rendering_fiber.resume | ||
end |
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.
🛠️ Refactor suggestion
Add error handling for Fiber operations.
While the implementation is solid, consider adding error handling for Fiber operations to gracefully handle failures:
def stream_react_component(component_name, options = {})
unless ReactOnRails::Utils.react_on_rails_pro?
raise ReactOnRails::Error,
"You must use React on Rails Pro to use the stream_react_component method."
end
if @rorp_rendering_fibers.nil?
raise ReactOnRails::Error,
"You must call stream_view_containing_react_components to render the view containing the react component"
end
rendering_fiber = Fiber.new do
+ begin
stream = internal_stream_react_component(component_name, options)
stream.each_chunk do |chunk|
Fiber.yield chunk
end
+ rescue => e
+ @rorp_rendering_fibers.delete(rendering_fiber)
+ raise ReactOnRails::Error, "Error in streaming component #{component_name}: #{e.message}"
+ end
end
@rorp_rendering_fibers << rendering_fiber
# return the first chunk of the fiber
# It contains the initial html of the component
# all updates will be appended to the stream sent to browser
- rendering_fiber.resume
+ begin
+ rendering_fiber.resume
+ rescue FiberError => e
+ @rorp_rendering_fibers.delete(rendering_fiber)
+ raise ReactOnRails::Error, "Failed to start streaming component #{component_name}: #{e.message}"
+ end
end
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
# Streams a server-side rendered React component using React's `renderToPipeableStream`. | |
# Supports React 18 features like Suspense, concurrent rendering, and selective hydration. | |
# Enables progressive rendering and improved performance for large components. | |
# | |
# Note: This function can only be used with React on Rails Pro. | |
# The view that uses this function must be rendered using the | |
# `stream_view_containing_react_components` method from the React on Rails Pro gem. | |
# | |
# Example of an async React component that can benefit from streaming: | |
# | |
# const AsyncComponent = async () => { | |
# const data = await fetchData(); | |
# return <div>{data}</div>; | |
# }; | |
# | |
# function App() { | |
# return ( | |
# <Suspense fallback={<div>Loading...</div>}> | |
# <AsyncComponent /> | |
# </Suspense> | |
# ); | |
# } | |
# | |
# @param [String] component_name Name of your registered component | |
# @param [Hash] options Options for rendering | |
# @option options [Hash] :props Props to pass to the react component | |
# @option options [String] :dom_id DOM ID of the component container | |
# @option options [Hash] :html_options Options passed to content_tag | |
# @option options [Boolean] :prerender Set to false to disable server-side rendering | |
# @option options [Boolean] :trace Set to true to add extra debugging information to the HTML | |
# @option options [Boolean] :raise_on_prerender_error Set to true to raise exceptions during server-side rendering | |
# Any other options are passed to the content tag, including the id. | |
def stream_react_component(component_name, options = {}) | |
unless ReactOnRails::Utils.react_on_rails_pro? | |
raise ReactOnRails::Error, | |
"You must use React on Rails Pro to use the stream_react_component method." | |
end | |
if @rorp_rendering_fibers.nil? | |
raise ReactOnRails::Error, | |
"You must call stream_view_containing_react_components to render the view containing the react component" | |
end | |
rendering_fiber = Fiber.new do | |
stream = internal_stream_react_component(component_name, options) | |
stream.each_chunk do |chunk| | |
Fiber.yield chunk | |
end | |
end | |
@rorp_rendering_fibers << rendering_fiber | |
# return the first chunk of the fiber | |
# It contains the initial html of the component | |
# all updates will be appended to the stream sent to browser | |
rendering_fiber.resume | |
end | |
def stream_react_component(component_name, options = {}) | |
unless ReactOnRails::Utils.react_on_rails_pro? | |
raise ReactOnRails::Error, | |
"You must use React on Rails Pro to use the stream_react_component method." | |
end | |
if @rorp_rendering_fibers.nil? | |
raise ReactOnRails::Error, | |
"You must call stream_view_containing_react_components to render the view containing the react component" | |
end | |
rendering_fiber = Fiber.new do | |
begin | |
stream = internal_stream_react_component(component_name, options) | |
stream.each_chunk do |chunk| | |
Fiber.yield chunk | |
end | |
rescue => e | |
@rorp_rendering_fibers.delete(rendering_fiber) | |
raise ReactOnRails::Error, "Error in streaming component #{component_name}: #{e.message}" | |
end | |
end | |
@rorp_rendering_fibers << rendering_fiber | |
# return the first chunk of the fiber | |
# It contains the initial html of the component | |
# all updates will be appended to the stream sent to browser | |
begin | |
rendering_fiber.resume | |
rescue FiberError => e | |
@rorp_rendering_fibers.delete(rendering_fiber) | |
raise ReactOnRails::Error, "Failed to start streaming component #{component_name}: #{e.message}" | |
end | |
end |
def build_react_component_result_for_server_streamed_content( | ||
rendered_html_stream: required("rendered_html_stream"), | ||
component_specification_tag: required("component_specification_tag"), | ||
render_options: required("render_options") | ||
) | ||
content_tag_options_html_tag = render_options.html_options[:tag] || "div" | ||
# The component_specification_tag is appended to the first chunk | ||
# We need to pass it early with the first chunk because it's needed in hydration | ||
# We need to make sure that client can hydrate the app early even before all components are streamed | ||
is_first_chunk = true | ||
rendered_html_stream = rendered_html_stream.transform do |chunk| | ||
if is_first_chunk | ||
is_first_chunk = false | ||
html_content = <<-HTML | ||
#{rails_context_if_not_already_rendered} | ||
#{component_specification_tag} | ||
<#{content_tag_options_html_tag} id="#{render_options.dom_id}">#{chunk}</#{content_tag_options_html_tag}> | ||
HTML | ||
next html_content.strip | ||
end | ||
chunk | ||
end | ||
|
||
rendered_html_stream.transform(&:html_safe) | ||
# TODO: handle console logs | ||
end |
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.
Sanitize HTML tag name to prevent XSS.
The content_tag_options_html_tag
is used directly in string interpolation which could be vulnerable to XSS if the tag name contains malicious content.
-content_tag_options_html_tag = render_options.html_options[:tag] || "div"
+content_tag_options_html_tag = render_options.html_options[:tag].presence&.to_s&.strip&.downcase || "div"
+unless content_tag_options_html_tag.match?(/\A[a-z0-9]+\z/)
+ raise ReactOnRails::Error, "Invalid HTML tag name: #{content_tag_options_html_tag}"
+end
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
def build_react_component_result_for_server_streamed_content( | |
rendered_html_stream: required("rendered_html_stream"), | |
component_specification_tag: required("component_specification_tag"), | |
render_options: required("render_options") | |
) | |
content_tag_options_html_tag = render_options.html_options[:tag] || "div" | |
# The component_specification_tag is appended to the first chunk | |
# We need to pass it early with the first chunk because it's needed in hydration | |
# We need to make sure that client can hydrate the app early even before all components are streamed | |
is_first_chunk = true | |
rendered_html_stream = rendered_html_stream.transform do |chunk| | |
if is_first_chunk | |
is_first_chunk = false | |
html_content = <<-HTML | |
#{rails_context_if_not_already_rendered} | |
#{component_specification_tag} | |
<#{content_tag_options_html_tag} id="#{render_options.dom_id}">#{chunk}</#{content_tag_options_html_tag}> | |
HTML | |
next html_content.strip | |
end | |
chunk | |
end | |
rendered_html_stream.transform(&:html_safe) | |
# TODO: handle console logs | |
end | |
def build_react_component_result_for_server_streamed_content( | |
rendered_html_stream: required("rendered_html_stream"), | |
component_specification_tag: required("component_specification_tag"), | |
render_options: required("render_options") | |
) | |
content_tag_options_html_tag = render_options.html_options[:tag].presence&.to_s&.strip&.downcase || "div" | |
unless content_tag_options_html_tag.match?(/\A[a-z0-9]+\z/) | |
raise ReactOnRails::Error, "Invalid HTML tag name: #{content_tag_options_html_tag}" | |
end | |
# The component_specification_tag is appended to the first chunk | |
# We need to pass it early with the first chunk because it's needed in hydration | |
# We need to make sure that client can hydrate the app early even before all components are streamed | |
is_first_chunk = true | |
rendered_html_stream = rendered_html_stream.transform do |chunk| | |
if is_first_chunk | |
is_first_chunk = false | |
html_content = <<-HTML | |
#{rails_context_if_not_already_rendered} | |
#{component_specification_tag} | |
<#{content_tag_options_html_tag} id="#{render_options.dom_id}">#{chunk}</#{content_tag_options_html_tag}> | |
HTML | |
next html_content.strip | |
end | |
chunk | |
end | |
rendered_html_stream.transform(&:html_safe) | |
# TODO: handle console logs | |
end |
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 and nitpick comments (1)
CHANGELOG.md (1)
21-26
: Enhance the changelog entry with more details.The changelog entry for streaming support should include:
- React version compatibility requirements
- Migration steps for users upgrading from previous versions
- More specific performance benefits and use cases
- Any breaking changes or deprecations
Consider expanding the entry like this:
### Added - Added streaming server rendering support: - New `stream_react_component` helper for adding streamed components to views - New `streamServerRenderedReactComponent` function in the react-on-rails package that uses React 18's `renderToPipeableStream` API - Enables progressive page loading and improved performance for server-rendered React components + - Requirements: React 18+ and Node 16+ + - Migration: Replace `react_component` with `stream_react_component` for components that benefit from streaming + - Performance benefits: + - Faster Time to First Byte (TTFB) + - Progressive hydration of components + - Improved handling of large server-rendered pages + - Note: Non-streaming rendering still supported for backward compatibility
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
📒 Files selected for processing (1)
- CHANGELOG.md (1 hunks)
🔇 Additional comments (1)
CHANGELOG.md (1)
26-26
: LGTM! Changelog formatting is consistent.The entry follows the established changelog format with proper PR reference and contributor attribution.
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: 2
🧹 Outside diff range and nitpick comments (4)
docs/guides/streaming-server-rendering.md (4)
25-30
: Consider revising the React version requirements.
Specifying an exact canary version in documentation may lead to maintenance issues and confusion. Consider:
- Using a stable version range instead of a canary build
- Adding a warning about experimental features
- Clarifying the minimum required versions
"dependencies": {
- "react": "18.3.0-canary-670811593-20240322",
- "react-dom": "18.3.0-canary-670811593-20240322"
+ "react": "^18.2.0",
+ "react-dom": "^18.2.0"
}
83-95
: Add important configuration details to the controller example.
The controller example should include additional configuration details such as:
- Response headers for streaming
- Error handling for broken connections
- Cleanup of resources
class ExampleController < ApplicationController
include ActionController::Live
include ReactOnRails::Controller
include ReactOnRailsPro::Stream
def show
+ # Set headers for streaming
+ response.headers['Content-Type'] = 'text/html; charset=utf-8'
+ response.headers['Transfer-Encoding'] = 'chunked'
+
stream_view_containing_react_components(template: 'example/show')
+ rescue ClientDisconnected
+ # Handle client disconnection
+ logger.info "Client disconnected"
+ ensure
+ # Cleanup resources
+ response.stream.close
end
end
139-145
: Enhance client-side script example with error handling and security considerations.
The current script example is oversimplified and could be improved with proper error handling and security considerations.
<script>
- // This implementation is slightly simplified
- document.getElementById('s0').replaceChildren(
- document.getElementById('b0')
- );
+ // Safely update content with error handling
+ try {
+ const target = document.getElementById('s0');
+ const content = document.getElementById('b0');
+ if (target && content) {
+ // Sanitize content if needed
+ target.replaceChildren(content.content.cloneNode(true));
+ }
+ } catch (error) {
+ console.error('Error updating streaming content:', error);
+ }
</script>
168-196
: Enhance best practices section with additional considerations.
Consider adding these important aspects to the best practices section:
- Performance implications:
- Memory usage on the server
- Impact on Time to Interactive (TTI)
- Browser support considerations
- SEO implications of streaming content
Would you like me to provide detailed content for these additional sections?
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
📒 Files selected for processing (2)
- SUMMARY.md (1 hunks)
- docs/guides/streaming-server-rendering.md (1 hunks)
🧰 Additional context used
🪛 Markdownlint
SUMMARY.md
20-20: Expected: 0; Actual: 2
Unordered list indentation
(MD007, ul-indent)
🪛 LanguageTool
docs/guides/streaming-server-rendering.md
[uncategorized] ~114-~114: If this is a compound adjective that modifies the following noun, use a hyphen.
Context: ...browser renders these updates without a full page reload For example, with our `MyStream...
(EN_COMPOUND_ADJECTIVE_INTERNAL)
[duplication] ~153-~153: Possible typo: you repeated a word
Context: ...: ### Ideal Use Cases 1. Data-Heavy Pages - Pages that fetch data from multiple sources ...
(ENGLISH_WORD_REPEAT_RULE)
[duplication] ~163-~163: Possible typo: you repeated a word
Context: ...nt priority levels 3. Large, Complex Applications - Applications with multiple independent widgets or co...
(ENGLISH_WORD_REPEAT_RULE)
🔇 Additional comments (1)
SUMMARY.md (1)
20-20
: Verify the linked documentation file exists.
The new entry for streaming server rendering is well-placed under the "Basics" section. However, let's ensure the linked documentation file exists.
✅ Verification successful
Link to streaming server rendering documentation is valid ✅
The linked documentation file docs/guides/streaming-server-rendering.md
exists at the specified path, confirming that the new entry in SUMMARY.md
correctly references an existing guide.
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# Description: Verify the existence of the streaming server rendering documentation file
# Expected: File should exist at the specified path
fd "streaming-server-rendering.md" docs/guides/
Length of output: 89
🧰 Tools
🪛 Markdownlint
20-20: Expected: 0; Actual: 2
Unordered list indentation
(MD007, ul-indent)
@@ -17,6 +17,7 @@ Here is the new link: | |||
+ [How React on Rails Works](docs/outdated/how-react-on-rails-works.md) | |||
+ [Client vs. Server Rendering](./docs/guides/client-vs-server-rendering.md) | |||
+ [React Server Rendering](./docs/guides/react-server-rendering.md) | |||
+ [🚀 Next-Gen Server Rendering: Streaming with React 18's Latest APIs](./docs/guides/streaming-server-rendering.md) |
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.
Fix indentation to match other list items.
The indentation of this line doesn't match other items in the "Basics" section.
Apply this diff to fix the indentation:
- + [🚀 Next-Gen Server Rendering: Streaming with React 18's Latest APIs](./docs/guides/streaming-server-rendering.md)
+ + [🚀 Next-Gen Server Rendering: Streaming with React 18's Latest APIs](./docs/guides/streaming-server-rendering.md)
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
+ [🚀 Next-Gen Server Rendering: Streaming with React 18's Latest APIs](./docs/guides/streaming-server-rendering.md) | |
+ [🚀 Next-Gen Server Rendering: Streaming with React 18's Latest APIs](./docs/guides/streaming-server-rendering.md) |
🧰 Tools
🪛 Markdownlint
20-20: Expected: 0; Actual: 2
Unordered list indentation
(MD007, ul-indent)
const MyStreamingComponent = () => { | ||
return ( | ||
<header> | ||
<h1>Streaming Server Rendering</h1> | ||
</header> | ||
<Suspense fallback={<div>Loading...</div>}> | ||
<SlowDataComponent /> | ||
</Suspense> | ||
); | ||
}; |
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.
Fix JSX syntax error in the example component.
The component's JSX needs to be wrapped in a parent element or fragment as React components must return a single root element.
const MyStreamingComponent = () => {
return (
+ <>
<header>
<h1>Streaming Server Rendering</h1>
</header>
<Suspense fallback={<div>Loading...</div>}>
<SlowDataComponent />
</Suspense>
+ </>
);
};
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
const MyStreamingComponent = () => { | |
return ( | |
<header> | |
<h1>Streaming Server Rendering</h1> | |
</header> | |
<Suspense fallback={<div>Loading...</div>}> | |
<SlowDataComponent /> | |
</Suspense> | |
); | |
}; | |
const MyStreamingComponent = () => { | |
return ( | |
<> | |
<header> | |
<h1>Streaming Server Rendering</h1> | |
</header> | |
<Suspense fallback={<div>Loading...</div>}> | |
<SlowDataComponent /> | |
</Suspense> | |
</> | |
); | |
}; |
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
🧹 Outside diff range and nitpick comments (5)
docs/guides/streaming-server-rendering.md (5)
26-31
: Consider using a more flexible version specification for experimental React.
The current specific canary version might become outdated quickly. Consider adding a note about version compatibility or using a version range.
"dependencies": {
- "react": "18.3.0-canary-670811593-20240322",
- "react-dom": "18.3.0-canary-670811593-20240322"
+ "react": "^18.3.0-canary",
+ "react-dom": "^18.3.0-canary"
}
82-89
: Add CSRF token handling to the Rails integration example.
The example should demonstrate how to handle CSRF tokens when making API requests.
<%=
stream_react_component(
'MyStreamingComponent',
props: { greeting: 'Hello, Streaming World!' },
- prerender: true
+ prerender: true,
+ csrf_token: form_authenticity_token
)
%>
113-116
: Enhance testing instructions section.
The testing section would benefit from more detailed instructions, including:
- How to write tests for streaming components
- How to debug streaming issues
- How to verify proper streaming behavior in development
Would you like me to help generate a more comprehensive testing guide section?
163-183
: Add production deployment considerations.
The "When to Use Streaming" section should include information about:
- Production server requirements
- Load balancer configurations
- Monitoring and debugging in production
- Performance metrics to track
Would you like me to help draft a production deployment subsection?
🧰 Tools
🪛 LanguageTool
[duplication] ~169-~169: Possible typo: you repeated a word
Context: ...: ### Ideal Use Cases 1. Data-Heavy Pages - Pages that fetch data from multiple sources ...
(ENGLISH_WORD_REPEAT_RULE)
[duplication] ~179-~179: Possible typo: you repeated a word
Context: ...nt priority levels 3. Large, Complex Applications - Applications with multiple independent widgets or co...
(ENGLISH_WORD_REPEAT_RULE)
130-130
: Fix compound adjective hyphenation.
Change "full page reload" to "full-page reload" as it's a compound adjective modifying a noun.
🧰 Tools
🪛 LanguageTool
[uncategorized] ~130-~130: If this is a compound adjective that modifies the following noun, use a hyphen.
Context: ...browser renders these updates without a full page reload For example, with our `MyStream...
(EN_COMPOUND_ADJECTIVE_INTERNAL)
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
📒 Files selected for processing (1)
- docs/guides/streaming-server-rendering.md (1 hunks)
🧰 Additional context used
🪛 LanguageTool
docs/guides/streaming-server-rendering.md
[uncategorized] ~130-~130: If this is a compound adjective that modifies the following noun, use a hyphen.
Context: ...browser renders these updates without a full page reload For example, with our `MyStream...
(EN_COMPOUND_ADJECTIVE_INTERNAL)
[duplication] ~169-~169: Possible typo: you repeated a word
Context: ...: ### Ideal Use Cases 1. Data-Heavy Pages - Pages that fetch data from multiple sources ...
(ENGLISH_WORD_REPEAT_RULE)
[duplication] ~179-~179: Possible typo: you repeated a word
Context: ...nt priority levels 3. Large, Complex Applications - Applications with multiple independent widgets or co...
(ENGLISH_WORD_REPEAT_RULE)
const fetchData = async () => { | ||
// Simulate API call | ||
const response = await fetch('api/endpoint'); | ||
return response.json(); | ||
}; |
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.
Add error handling to the fetchData
function.
The current implementation doesn't handle API errors, which could lead to unhandled promise rejections.
const fetchData = async () => {
- // Simulate API call
- const response = await fetch('api/endpoint');
- return response.json();
+ try {
+ const response = await fetch('api/endpoint');
+ if (!response.ok) {
+ throw new Error(`HTTP error! status: ${response.status}`);
+ }
+ return response.json();
+ } catch (error) {
+ console.error('Error fetching data:', error);
+ throw error;
+ }
};
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
const fetchData = async () => { | |
// Simulate API call | |
const response = await fetch('api/endpoint'); | |
return response.json(); | |
}; | |
const fetchData = async () => { | |
try { | |
const response = await fetch('api/endpoint'); | |
if (!response.ok) { | |
throw new Error(`HTTP error! status: ${response.status}`); | |
} | |
return response.json(); | |
} catch (error) { | |
console.error('Error fetching data:', error); | |
throw error; | |
} | |
}; |
Summary
This PR introduces the new features introduced in React 18 which is supporting Async Components and streaming components. It's done by replacing renderToString function with renderToPipeableStream.
https://www.loom.com/share/db55036514b746a7b283e6f296134168?sid=2ce8205d-6956-4e9a-97bc-ee55004f3216
The Dummy App
To open the newly introduced page in the dummy app, you need to clone both React on Rails and React on Rails Pro on your local machine and put them in the same directory
execute the following commands
Then, open the newly introduced
stream_async_components
in the dummy app.More resources
https://react.dev/reference/react-dom/server/renderToPipeableStream
reactwg/react-18#37
Sequence Diagrams
Summary by CodeRabbit
Summary by CodeRabbit
New Features
Bug Fixes
Updates
This change is