Implement SSR with React 18 (pipeToNodeWritable) + react redux - reactjs

As you know the react 18 alpha version which was just released includes :
Suspense and lazy on the server: You can now use and lazy
on the server. Together with the new pipeToNodeWritable method, this
solves long-standing pain points and performance problems with server
rendering:
Code splitting works together with SSR. You don't have to choose one
or the other. React uses your boundaries to stream the page
HTML in visual chunks. React uses your boundaries to
hydrate the page in chunks, improving responsiveness.
More details there : https://github.com/reactwg/react-18/discussions/47
Dan Abramov has developed a demo https://codesandbox.io/s/festive-star-9hfqt?file=/server/render.js
showing how we can set up SSR with react using pipeToNodeWritable, though it is said
we don't have a recommendation yet for how to transfer data from the
server to the client to prepopulate the cache
In a typical SSR app in react/react router, you find the route matching the request, and then you await on all the promises that are within your routes's components.
If you you use redux, you can then set the data into the store and render the html.
Something like that : https://github.com/ilkeraltin/react-ssr-news/blob/master/src/index.js#L42
My question is how would that fit into Dan Abramov's demo ?
In the render.js file there's a createServerData () function, would that be the location where we basically do, what I described above ? (await on route's promises)

Related

Any difference between renderToPipeableStream vs renderToNodeStream if no suspense was used

I understand that we use Suspense to mark the boundary. If there is no suspense, that means the render will be done in just a single pass, which is the same as the SSR without streaming.
Are we getting any benefit from using renderToPieableStream - the new stream, if there is no Suspense usage in the codebase at all?
Also, would that mean renderToNodeStream would be doing the same thing?
There's no benefit to using renderToPipeableStream() in React 18 if you are not using the Suspense component.
However, there is a major benefit to using renderToPipeableStream() instead of renderToNodeStream() because the latter is actually being depreciated - it also didn't work very well since it could not wait for data.
renderToNodeStream() was basically one step closer to full SSR than renderToString() but still not quite there.
This React 18 Github announcement highlights everything really well.
https://github.com/reactwg/react-18/discussions/22
Hope that helps!
Suspense tells react that there is an async component is getting prepared so when the async component finished data fetching, react will inject that part into the correct HTML spot.
Imagine an Amazon product page, we first see the product images and details about the product and then we see the comments, reviews, footer etc. down below. with renderToPipeableStream() we can first send the top part, images, and details about the product and then rest of page can be loaded. this way you can interact with the part that is loaded while the rest is loading. Because after this part is loaded, react injects its script. this stream is writable stream
export function renderToPipeableStream(children: ReactNode, options?: RenderToPipeableStreamOptions): PipeableStream;
export interface PipeableStream {
abort(): void;
pipe<Writable extends NodeJS.WritableStream>(destination: Writable): Writable}
with renderToNodeStream, you start to send the first bit of HTML to the client, client will see the content but will not be able to interact with it till the streaming is fully done and then the client hydrates it, injects the script. If you were using renderToNodeStream, you had to go over all the pages to see if they had a data fetching function, you had to finish data fetching for all the pages and then you were going to start streaming. this stream is a readable stream
export function renderToNodeStream(element: ReactElement): NodeJS.ReadableStream;

How to resolve React suspense on the server

In my web app I would like to render most of my content on the server and serve complete HTML. Also, I would like for each React component to fetch its own data. I don't like the Next.js' approach of "fetch the data for the whole page". React suspense seems to be a good tool for this but suspense is never resolved on the server. The best you can do with React 18 is to renderToPipeableStream but then the actual content is still being constructed on the client by injecting script tags. That's not what I want. I want plain old HTML generated on the server.
I am now using react-ssr-prepass which works fine with React 17 but it is not suited to React 18.
Is there some way to achieve this? It seems to me it would be quite common usecase.
Check react-streaming. It's the only library that helps at the moment but it's still experimental

React Hydration - How can we hydrate a dynamic page that not fully available at build time?

I am building a website using Express and React. I go with Server-side rendering
Now I am looking for a solution to hydrate my final HTML.
The issue is at the build time, the page is still not available. A page only started with a main component and Express middlewares can add/remove sub-components to that main component. It means we only know what is the final structure of the page at the end of the request
Is there any way I can hydrate that page and make React available in the client browser?
Thank you so much

Server side rendering with devextreme and material ui #react16

After refreshing the page (and going through my ssr) it looks like none of the css is sustained unless I navigate through my app and get back to it Or even click some elements. Are there any examples of how this is done correctly?
Im using the exact same code from the controlled 'react material ui grid' example:
https://github.com/kkotwal94/DrivingService (develop branch) <- where the component is under components / demoBase, and the SSR is under server/render/pageRender.jsx. I use the material ui example for how this is done. I utilize demo grid in Students.jsx.
Here is a pic of what happens post refresh:
Everything else renders fine (all other pages) in production mode and dev mode. I have no clue what im missing here. It looks like the jss-in-css is mapping incorrectly.
I found that reverting back to pre-React 16 everything began to work again SSR and what not, however i cant use dx-react-grid project since it requires 16. Kind of in a wackamole, still investigating where i goofed.
TEST
http://transportation.kkotwal.me/
I hosted it, if you click on login you can log in with yea#yea.com, password: 123, or you can just sign up where the username has to be a email it doesnt matter. After wards if you navigate to the students button on the navigation (if you click on transportation tracker after logging in you should be back to the root page / view). You will see the dev extreme controlled grid example.
If you hit refresh on that page you will see all the css is messed up. In case you arent sure what the page is: http://transportation.kkotwal.me/students. The source is here: https://github.com/kkotwal94/DrivingService/tree/UpdateReact . The server side rendering is located https://github.com/kkotwal94/DrivingService/tree/UpdateReact/server/render. The component for the devExtreme component is called DemoBase.jsx in the components folder, and the container that renders this is https://github.com/kkotwal94/DrivingService/blob/UpdateReact/app/containers/students/Students.jsx.
I guess you're already aware that React 16 came with lots of improvements to server-side rendering. The update came with additional server-side render methods like renderToNodeStream().
The official guide on upgrading React from 15 to 16 mentions that it should have no issues, with minor exceptions. One of those exceptions is a break change exactly when you hydrate a server-rendered container:
Hydrating a server-rendered container now has an explicit API. If you’re reviving server-rendered HTML, use ReactDOM.hydrate instead of ReactDOM.render. Keep using ReactDOM.render if you’re just doing client-side rendering.
Having that in mind, I'd search in your project (and possibly in third-party libraries as well) for some ReactDOM.render that was missed to be changed to ReactDOM.hydrate while upgrading React to version 16.
this is probably the issue at server side code and your nodejs script.
Reason #1:
if you are using material ui version 4.x then you should look at their ssr documentation
in material ui version 3.x or below that we use
JssProvider from 'react jss/lib/JssProvider';
however this is no more required, your both github links are broken , kindly check ssr code of yours and compare it with material-ui documentation
Reason #2:
you have to refer to your build folder for your expressjs
app.use(express.static(path.join(__dirname, '../../build')));
app.use(express.static(path.join(__dirname, 'public')));
this could be another reason and if this is missing then check that your componentDidMount also will not be invoked, so client side rendering won't be happening, however for ssr both client side and server side rendering has to happen
For complete code on SSR kindly refer this link

How to add universal support existing react project

I've created a react redux project, now how do I add some SEO functionalities to project? I do not want to waste much time while refactoring codes.
You need to setup the redux store on the server and pass its initial state down to the client, so that the server-render and initial client render don't differ
Not all life cycle functions are called on the serverside, mainly componentDidMount is not called. This indeed helps if you want to do some AJAX data fetching (which you only want to do on the client).
If you are using react-router you need to setup the serverside route matching
Here are some more details: React can be used on server side rendering. What does that mean?

Resources