Skip to content

Add support for streaming rendered components using renderToPipeableStream#1633

Merged
AbanoubGhadban merged 25 commits intomasterfrom
abanoubghadban/pro403-use-render-to-readable-stream-to-render-async
Oct 29, 2024
Merged

Add support for streaming rendered components using renderToPipeableStream#1633
AbanoubGhadban merged 25 commits intomasterfrom
abanoubghadban/pro403-use-render-to-readable-stream-to-render-async

Conversation

@AbanoubGhadban
Copy link
Copy Markdown
Collaborator

@AbanoubGhadban AbanoubGhadban commented Jun 17, 2024

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

  • <parent_directory>
    • react_on_rails
    • react_on_rails_pro

execute the following commands

cd <parent_directory>
cd react_on_rails
bundle && yarn
cd ../react_on_rails_pro
bundle && yarn
cd spec/dummy
bundle && yarn

bin/dev

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

sequenceDiagram
    participant Browser
    participant RailsServer
    participant ReactOnRails (Ruby)
    participant ReactOnRailsPro (JS)
    participant ReactDOMServer
    Browser->>RailsServer: Request page with React component
    RailsServer->>ReactOnRails (Ruby): Call `stream_react_component`
    ReactOnRails (Ruby)->>ReactOnRailsPro (JS): Trigger `streamServerRenderedReactComponent`
    ReactOnRails (JS)->>ReactDOMServer: Call `renderToPipeableStream`
    ReactDOMServer-->>ReactOnRailsPro (JS): PipeableStream
    ReactOnRails (JS)-->>ReactOnRails (Ruby): Stream content
    ReactOnRails (Ruby)-->>RailsServer: Streamed React component content
    RailsServer-->>Browser: Serve content
    Note right of Browser: Displays page with streamed React component
Loading

Summary by CodeRabbit

Summary by CodeRabbit

  • New Features

    • Introduced streaming functionality for React components, enabling server-side rendering with enhanced performance and better error management.
    • Added methods for asynchronous streaming of React components and improved output formatting in the React on Rails helper module.
    • Implemented new functions to manage server-side rendering of React components to a stream.
    • Enhanced Webpack configuration to support Node.js globals and added new aliases for better compatibility.
    • Introduced a new method for accessing streaming options in the rendering process.
    • Added a comprehensive guide on implementing streaming server rendering using React 18's APIs.
  • Bug Fixes

    • Improved error handling during rendering for increased reliability.
    • Clarified method name for better understanding in the React on Rails module.
  • Updates

    • Upgraded React and React-DOM versions to 18.2.0 for improved support and additional features.
    • Updated test setup to ensure compatibility with jsdom environment, including a new polyfill for compatibility issues.
    • Improved version control cleanliness by ignoring IDE-related files and directories.
    • Updated documentation structure and links to reflect recent changes and enhancements.

This change is Reviewable

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Jun 17, 2024

Walkthrough

This update enhances the react_on_rails library by introducing robust server-side streaming capabilities for React components. Significant new methods have been added in both Ruby and JavaScript to facilitate asynchronous rendering, improve error handling, and ensure compatibility with the latest package versions. Additionally, the Webpack configuration has been adjusted to support these features, and various test setups have been refined.

Changes

Files/Paths Change Summary
jest.config.js Added setupFiles property for test suite configuration.
lib/react_on_rails/helper.rb Added streaming methods for React components and refactored existing methods for clarity and functionality.
lib/react_on_rails/react_component/render_options.rb, lib/react_on_rails/server_rendering_pool/ruby_embedded_java_script.rb Added new methods for streaming options and JavaScript rendering capabilities.
node_package/src/ReactOnRails.ts, node_package/src/types/index.ts, node_package/src/serverRenderReactComponent.ts Introduced streamServerRenderedReactComponent for server-side React streaming; updated interface for new functionality.
node_package/tests/ReactOnRails.test.js Improved test setup for rendering checks.
node_package/tests/jest.setup.js Introduced a polyfill for TextEncoder and TextDecoder.
package.json Upgraded react and react-dom versions to 18.2.0.
spec/dummy/config/webpack/alias.js, spec/dummy/config/webpack/commonWebpackConfig.js, spec/dummy/config/webpack/webpackConfig.js Configured webpack to support streaming with stream-browserify and added Node.js globals compatibility.
spec/dummy/spec/helpers/react_on_rails_helper_spec.rb Added tests for rails_context_if_not_already_rendered method to ensure correct behavior and output.

Possibly related PRs

  • should force load react-components which send over turbo-stream #1620: The main PR introduces a new property setupFiles in jest.config.js, which is related to the configuration of test setups. This is relevant to the changes in the lib/react_on_rails/helper.rb file, where the loading behavior of React components is enhanced, potentially impacting how tests are set up and executed.
  • Refactor serverRenderReactComponent function #1653: The refactor of the serverRenderReactComponent function in this PR may relate to the changes in the main PR as both involve modifications to the server rendering logic, which could affect how tests are structured and executed in relation to server-rendered components.

Suggested reviewers

  • alexeyr-ci: Suggested for review due to expertise in the react_on_rails library.
  • justin808: Suggested for review due to familiarity with the react_on_rails library.
  • Judahmeek: Suggested for review given experience with server-side rendering implementations.

Poem

In the garden where bytes flow,
A new feature begins to grow.
Streaming React with seamless grace,
Swiftly dancing in data's embrace.
Rails and JavaScript, hand in hand,
Server to client, perfectly planned.
🌱🚀🎉


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?

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

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)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a 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

Commits

Files that changed from the base of the PR and between 2f023ec and 151c33b.

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 the stream module using stream-browserify is correctly set up to ensure compatibility across environments.

lib/react_on_rails/react_component/render_options.rb (1)

106-108: The stream? 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: Importing PassThrough from stream is correctly added to support the types used in the new streaming functions.


141-141: The addition of streamServerRenderedReactComponent to the ReactOnRails interface correctly extends its capabilities to support streaming.

node_package/src/serverRenderReactComponent.ts (1)

74-99: The implementation of streaming using ReactDOMServer.renderToPipeableStream and handling the stream with PassThrough 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 in stream_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.

Comment on lines +237 to +239
if (throwJsErrors) {
throw e;
}

renderResult = stringToStream(handleError({
e,
name,
serverSide: true,
}));
Copy link
Copy Markdown
Contributor

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}`);
}

Comment thread node_package/src/ReactOnRails.ts Outdated
@@ -1,10 +1,11 @@
import type { ReactElement } from 'react';
import type { PassThrough } from 'stream';
Copy link
Copy Markdown
Contributor

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

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a 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

Commits

Files that changed from the base of the PR and between 151c33b and 69ce6fd.

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

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a 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 for PassThrough.

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

Commits

Files that changed from the base of the PR and between 69ce6fd and 04a263b.

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 method streamServerRenderedReactComponent 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.ts

Length of output: 1322

Comment thread node_package/src/serverRenderReactComponent.ts Outdated
Comment thread node_package/src/serverRenderReactComponent.ts Outdated
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a 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: Function stream_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

Commits

Files that changed from the base of the PR and between 04a263b and 00c9552.

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 the stream_react_component_async method. This usage appears to be correct. However, to be thorough, we need to ensure there are no other occurrences of stream_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 the lib/react_on_rails/helper.rb file, specifically within the stream_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

@AbanoubGhadban AbanoubGhadban force-pushed the abanoubghadban/pro403-use-render-to-readable-stream-to-render-async branch from 00c9552 to fbb1060 Compare July 24, 2024 13:02
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a 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

Commits

Files that changed from the base of the PR and between 00c9552 and fbb1060.

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

@AbanoubGhadban AbanoubGhadban force-pushed the abanoubghadban/pro403-use-render-to-readable-stream-to-render-async branch from fbb1060 to 7bb828e Compare July 24, 2024 13:59
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a 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

Commits

Files that changed from the base of the PR and between fbb1060 and 7bb828e.

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 for stream to stream-browserify.

This addition correctly maps the stream module to stream-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 for stream to stream-browserify.

This addition correctly sets a fallback for the stream module to use stream-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 to 18.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: Upgrade react and react-dom to 18.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 the options hash.

node_package/src/types/index.ts (2)

2-2: LGTM!

The import statement for PassThrough from the stream module is correct and necessary for the new method.


141-141: LGTM!

The method streamServerRenderedReactComponent correctly returns a PassThrough stream and enhances the functionality of the ReactOnRails interface.

lib/react_on_rails/server_rendering_pool/ruby_embedded_java_script.rb (1)

96-99: LGTM! But remember to merge with exec_server_render_js.

The method exec_server_render_streaming_js correctly invokes the eval_streaming_js method. The TODO comment indicates an intention to merge this method with exec_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 a stream? flag and calls internal_react_component. The function also correctly invokes build_react_component_result_for_server_streamed_content.


104-122: LGTM!

The stream_react_component_async function correctly uses Ruby's Fiber 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.

Comment thread node_package/src/ReactOnRails.ts Outdated
Comment on lines +249 to +257
streamServerRenderedReactComponent(options: RenderParams): PassThrough {
return streamServerRenderedReactComponent(options);
},
Copy link
Copy Markdown
Contributor

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.

Comment on lines +394 to +458
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
Copy link
Copy Markdown
Contributor

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?

Comment thread lib/react_on_rails/helper.rb Outdated
Comment thread lib/react_on_rails/helper.rb Outdated

def stream_react_component(component_name, options = {})
options = options.merge(stream?: true)
result = internal_react_component(component_name, options)
Copy link
Copy Markdown
Collaborator Author

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|
Copy link
Copy Markdown
Collaborator Author

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

Comment thread lib/react_on_rails/helper.rb
Comment thread package.json
"prop-types": "^15.8.1",
"react": "^17.0.0",
"react-dom": "^17.0.0",
"react": "^18.2.0",
Copy link
Copy Markdown
Collaborator Author

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

Copy link
Copy Markdown
Collaborator Author

@AbanoubGhadban AbanoubGhadban left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Self-reviewed

Comment thread lib/react_on_rails/helper.rb Outdated
Comment thread lib/react_on_rails/helper.rb
Comment thread lib/react_on_rails/helper.rb Outdated
Comment thread node_package/src/serverRenderReactComponent.ts Outdated
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a 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

Commits

Files that changed from the base of the PR and between 7bb828e and b71e866.

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: setupFiles

The 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 TextDecoder

The polyfills for TextEncoder and TextDecoder 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 Verification

The 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 using PassThrough.


177-220: LGTM!

The streamServerRenderedReactComponent function correctly implements the streaming of rendered React components using renderToPipeableStream.

Comment on lines +177 to +243
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;
};
Copy link
Copy Markdown
Contributor

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}`);
}

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a 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

Commits

Files that changed from the base of the PR and between b71e866 and f201f02.

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 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?


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 the stream_react_component_async method in the same file. To ensure thorough verification, we need to check if there are any other usages of stream_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 the stream_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

@AbanoubGhadban AbanoubGhadban force-pushed the abanoubghadban/pro403-use-render-to-readable-stream-to-render-async branch from 9ed4ce3 to 0c9955a Compare July 28, 2024 14:06

document.body.innerHTML = '<div id="root"></div>';
// eslint-disable-next-line no-underscore-dangle
const actual = ReactOnRails.render('R1', {}, 'root')._reactInternals.type;
Copy link
Copy Markdown
Collaborator Author

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.

@AbanoubGhadban
Copy link
Copy Markdown
Collaborator Author

Would it make sense to add async: false parameter to def stream_react_component instead of a separate function (probably not, but asking just in case)?

The stream_react_component_async is renamed now to stream_react_component. It's async by default. The other function is removed

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a 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

Commits

Files that changed from the base of the PR and between f201f02 and 58a70bf.

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 the ProvidePlugin ensures that the process 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 uses PassThrough 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 using renderToPipeableStream. 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 using handleError. This approach is consistent with the rest of the codebase.

  • node_package/src/serverRenderReactComponent.ts: Lines 177-220
Scripts 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 5

Length 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 5

Length 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 a stream? 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's Fiber 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 of raise 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 5

Length of output: 65682

Comment on lines +396 to +458
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
Copy link
Copy Markdown
Contributor

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?

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a 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

Commits

Files that changed from the base of the PR and between 58a70bf and eda5594.

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

@AbanoubGhadban AbanoubGhadban requested review from ahangarha, alexeyr-ci and justin808 and removed request for ahangarha and justin808 July 28, 2024 15:43
Comment thread lib/react_on_rails/helper.rb Outdated
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants