Skip to content

Conversation

@KhaledEmaraDev
Copy link

@KhaledEmaraDev KhaledEmaraDev commented Aug 10, 2023

This is an initial implementation of React Server Components (RSC). In this PR:

  1. I add a controller responsible for rendering any RSC.
  2. I add a route pointing to the previous controller to render and RSC with its props.
  3. I use the Webpack plugin to generate the Client and SSR manifests required for RSC to work and upload them to the node-renderer.
  4. Add setImmediate() to the node-renderer's context because it's being used by RSC.
  5. Add an RSC Demo using react-router-dom and the RSCRoute from react_on_rails to render a server component that does two things:
  6. Sanitize and Render some Markdown using marked and sanitize-html which combined are 75K gzipped. (They aren't shipped to the server)
  7. Use Apollo to make a GraphQL query that's done on the server and only the result is sent.

Limitations:

Newer React Server rendering are generally done using streams to one by one stream rendered content and support React's <Suspense>. The way we render right now one shots everything once it's done making <Suspense> useless on the server. Read here.

Related to

shakacode/react_on_rails#1573

Comment on lines 8 to 11
gem "react_on_rails", "13.3.4" # keep in sync with package.json files
# gem "react_on_rails", "13.3.4" # keep in sync with package.json files

# For local development
# gem "react_on_rails", path: "../react_on_rails"
gem "react_on_rails", path: "../react_on_rails"
Copy link
Contributor

Choose a reason for hiding this comment

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

Do not include in the PR!

import React, { Suspense } from 'react';

import { marked } from 'marked'; // 35.9K (11.2K gzipped)
import sanitizeHtml from 'sanitize-html'; // 206K (63.3K gzipped)
Copy link
Contributor

Choose a reason for hiding this comment

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

No point including comments which will get obsolete so easily.

Comment on lines 16 to 22
const allowedAttributes = Object.assign(
{},
sanitizeHtml.defaults.allowedAttributes,
{
img: ['alt', 'src'],
}
);
Copy link
Contributor

Choose a reason for hiding this comment

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

Could be simplified to

const allowedAttributes = { ...sanitizeHtml.defaults.allowedAttributes, img: ['alt', 'src'] };

as far as I can see.

Signed-off-by: Khaled Emara <[email protected]>
@@ -0,0 +1,17 @@
// @ts-ignore
Copy link
Author

Choose a reason for hiding this comment

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

We should use @ts-expect-error instead and write a comment about why it's ignored.

}) {
const [count, setCount] = useState(0);

const allowedTags = [...defaultAllowedTags, ...['img', 'h1', 'h2', 'h3']];
Copy link
Contributor

Choose a reason for hiding this comment

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

No need for the second spread, just [...defaultAllowedTags, 'img', 'h1', 'h2', 'h3'].


# TODO: RailsContext
js_code = ReactOnRailsPro::ServerRenderingJsCode.render_rsc(
props_string(params[:props] || {}).gsub("\u2028", '\u2028').gsub("\u2029", '\u2029'),
Copy link
Contributor

Choose a reason for hiding this comment

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

Why just these two? Add a comment?

Signed-off-by: Khaled Emara <[email protected]>
@@ -0,0 +1,11 @@
node_modules
lib/
webpack.config.js
Copy link
Contributor

Choose a reason for hiding this comment

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

It normally shouldn't be ignored, any reason why?

Copy link
Author

Choose a reason for hiding this comment

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

It's taken from the react-on-rails package. Webpack isn't used anyways.

Copy link
Contributor

Choose a reason for hiding this comment

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

IMHO better to limit to the files we actually want to ignore.

@@ -0,0 +1,12 @@
node_modules

lib
Copy link
Contributor

Choose a reason for hiding this comment

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

Better ignore only at root: /lib, this is quite a likely name for needed directories.

Copy link
Author

Choose a reason for hiding this comment

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

It's needed here too as tsc outputs here.

Copy link
Contributor

@alexeyr-ci alexeyr-ci Oct 13, 2023

Choose a reason for hiding this comment

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

No, I mean if you put /lib here it will only ignore this particular lib. If you have lib, it will ignore something like src/some_path/lib as well.

node_modules/
package.json
**/app/assets/webpack/
lib/*
Copy link
Contributor

Choose a reason for hiding this comment

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

Probably also /lib/* if it's supported here.

Copy link
Author

Choose a reason for hiding this comment

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

This is the main output directory there are no other output directories for this package.

Copy link
Contributor

Choose a reason for hiding this comment

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

The problem is not output but someone who names a directory inside sources lib later.

@@ -0,0 +1,265 @@
const acorn = require('acorn-loose');
Copy link
Contributor

Choose a reason for hiding this comment

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

Any reason why this file is JS instead of TS?

@@ -0,0 +1,3 @@
export default function context(this: void): Window | NodeJS.Global| typeof globalThis | void {
Copy link
Contributor

Choose a reason for hiding this comment

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

Why a void parameter?

// TODO: Check why importing "react-on-rails" is required.
// TODO: Check why we have to register using ReactOnRailsPro only.
// import statement added by react_on_rails:generate_packs rake task
import "./../generated/server-bundle-generated.js"
Copy link
Contributor

Choose a reason for hiding this comment

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

Just ../generated (remove ./)?

if (loading) return <p>Loading...</p>;
if (error) return <p>Error : {error.message}</p>;

const allowedTags = [...sanitizeHtml.defaults.allowedTags, ...['img', 'h1', 'h2', 'h3']];
Copy link
Contributor

Choose a reason for hiding this comment

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

Also no need for the second spread.

}

clientConfig.plugins.unshift(new ReactServerWebpackPlugin({
isServer: false, clientReferences: [
Copy link
Contributor

Choose a reason for hiding this comment

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

Move clientReferences to a separate line for readability (though Prettier will probably say that as well).

@justin808 justin808 changed the title Initital Implementation of RSC [WIP - on HOLD] Initital Implementation of RSC Aug 21, 2024
@justin808
Copy link
Member

@KhaledEmaraDev @AbanoubGhadban should be closed in favor of #422?

@alexeyr-ci alexeyr-ci closed this Nov 21, 2024
@Judahmeek Judahmeek deleted the rsc-demo branch December 3, 2024 17:53
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