import React from "react";
import { renderToString } from "react-dom/server";
import { onPageLoad } from "meteor/server-render";
import App from "/imports/Server.js";
onPageLoad(sink => {
sink.renderIntoElementById("app", renderToString(
<App location={sink.request.url} />
));
});
It shows us importing App from /imports/Server.js but what does that do? Is that a router? If not, what is it? We can see this on the client example too:
import React from "react";
import ReactDOM from "react-dom";
import { onPageLoad } from "meteor/server-render";
onPageLoad(async sink => {
const App = (await import("/imports/Client.js")).default;
ReactDOM.hydrate(
<App />,
document.getElementById("app")
);
});
What is imports/Client.js ? Is that a router? Is it a container? What is it? What is it for?
Sorry for my frustration but because there’s no mention of routing and no mention of what those files do, I’m very confused.
Yes, they are router. For example if you use react router, those files should look like:
import React from 'react';
import { Route, Switch } from 'react-router-dom';
import SomeComponent from '/path_to_component.js';
import OtherComponent from '/path_to_other_component.js';
export default (
<Switch>
<Route path="/" component={SomeComponent} />
<Route path="/other" component={OtherComponent} />
</Switch>
);
the client.js and server.js may be the same file. But normally, server.js has only some routes which you want to support server render. For example the route /admin you don’t need to make it to support server render.
the renderToString method executes the code with a fake server-side DOM and converts it to html, which is sent to the client on first load.
Then on the client, the hydrate method tries to keep as much of the DOM intact as it runs the app and swaps out the static html with live React components
OK, and before I start thanks so much for responding, but I’m not seeing how the server actually does that. So here’s my code without a router:
/server/server.js
import React from "react";
import { onPageLoad } from "meteor/server-render";
import { renderToString } from "react-dom/server";
import App from "/imports/ui/App.js";
onPageLoad(sink => {
const html = renderToString( <App /> );
sink.renderIntoElementById("app", html);
});
Which is almost a perfect copy and paste from the docs. However, my “App” is not a router, it’s just a stateless component:
/imports/ui/App.js
import React, { Component } from 'react';
export default class App extends Component {
render () {
return (
<div className="app-container">
<header>Meteor SSR Blog</header>
</div>
);
}
}
In this situation, there should be a component mounted to #app but there isn’t. So where does that trickle from server to client? If I put a router in Imports, all it can do is do client side routing… so what am I missing here?
I’m not really sure what you mean
When the user initially loads the page the client sends a HTTP request to the server which returns a plain HTTP response
I’ll try to explain by going step by step:
The user navigates to the website
The client sends a request for a URL to the server
The server generates static HTML and sends it to the client
The client receives static html which the user can see instantly
React starts up on the client and loads the components into memory
react-dom compares the app in memory to the static html and attaches the behaviour and event listeners it needs onto the static DOM to make it into a dynamic react app (This process is called Hydration)
If a client side router is used, further navigation is handled on the client side only
The benefit of SSR is that the user can see the page as soon as the html arrives, rather than after all the javacsript has loaded / compiled / run and generated the page client side
I withdrew my other two comments, because I think now I’ve got some better questions. First I’d like to just point out that I’ve done this without meteor before just using express, I’m just trying to figure out how it works on meteor. You have two points which are what I’m asking about:
Client sends a request for a URL to the server.
Client receives static html which the user can see instantly.
I understand that, what I don’t understand is that while following the meteor docs to a t, that is not happening. To me that is not happening because there needs to be something on the client that does your second to last bullet “react-dom compares the app in memory to the static html and attaches the behaviour and event listeners it needs onto the static DOM to make it into a dynamic react app (This process is called Hydration)”
Without that “hydration” the html should still have been served to the client a la the meteor docs. That is not happening. So the question is - where does the hydration have to occur. Does it happen in the router? If so does the router live in shared code? If no, then it happens on the client - how does the client know based on which route what component should react compare the html string to the react module?
I know these are nitpick and I apologize, but I’ve exhausted the documentation and don’t know where else to turn.
How are you creating the HTML that contains the container element with id="app"? Server rendering won’t work unless it’s rendering into static HTML, so Blaze templates (which create HTML elements dynamically on the client) are not sufficient, because the DOM nodes they eventually create (on the client) are not available to the server rendering code.
This is why the ReactNYC app uses the static-html package instead of blaze-html-templates in its .meteor/packages file.
Nope, sure didn’t. So I could render from the server initially but could not route from the serve after. Very disappointed in the lack of documentation for this.
this is SSR. SSR is only for the first page request of a user. If the page is loaded, the page changes happen on the client. The SSR code has nothing to do with reactivity.
There is no relation to withTracker.
Also, that is not showing children routes. In that example we’d have to render and entire new app layout for each route.
the example renders the full app html for the path that is given by the server request. It will render whatever route it needs to render:
<StaticRouter location={sink.request.url} > ...
As I said, I can get to that point fine, it’s doing actual routing that I cannot find documentation for or examples that utilize withTracker .
I don’t understand that statement. Do you have an issue with SSR or with client-side routing? And what has withTracker to do with it?
I’m sorry, but it feels like you’re arguing to win instead of to help. First, I provided you a link to what I was working on, in that link I walk through step by step what I’m doing - you have not referenced any of that.
Second, what you are showing for sink.request.url I already know and have shown to know that in what I provided. What I do not know is: how to show different container components or different views entirely by route from the server. I cannot be more clear than that. Your example fundamentally does not show that or prove that it’s possible.
allows you to create a container component which provides data to your presentational components.
So in SSR rendering, you would either have to have no reactivity as you show in your example, or you have no way of routing from the server with reactive data.
Of course, I am more than happy to be wrong, but you are not showing where or how I am wrong.
Ok i checked the link above and i looked through your tutorial there, but I don’t think that this is useful to describe your problem. You have not done the routing section yet, so i guess, you want to add that section (?).
Anyway. You have to understand first how to use react-router on the client. Make your app is working with routing, but without SSR first and show us some actual code.
Also, SSR couldn’t care less if you try to render simple components or complex data-fetching containers. That’s why i say withTracker is irrelevant.
Your example fundamentally does not show that or prove that it’s possible.
So in SSR rendering, you would either have to have no reactivity as you show in your example, or you have no way of routing from the server with reactive data.
I don’t understand that statement.
Remember, SSR only happens on first page load. This is not reactive, its a request, client gets a response (the full rendered html), with whatever data you load in your components. If one of your component’s happen to load data from meteor collections using withTracker, it will load directly from the database without beeing reactive.
That beeing sad, you will run into some challenges. E.g. you will notice:
the server response might render components using more data than the client. This is because the server does not use publicatoins
after hydration, the client will suddenly have no data anymore. This is because collections don’t have a “hydration” concept, they get filled by subscriptions. For this you will need https://atmospherejs.com/?q=staringatlights:fast-render