Hide preloaded state in .js file? - node.js

When I do SSR in my React apps (using Node.js / Express), and using Redux for state, I always end up with a similar to window.__PRELOADED_STATE__ = {....}
This output is something rendered together with the generated html code from renderToString but I'm wondering if there is a way to get that preloaded state variable inside a "virtual" js file instead (that can be referenced in my main layout template file), just to make the source code cleaner, without having to do the same rendering process twice?
I currently do my SSR via a middleware that I've setup in my Node.js/Express app.
Anyone has any ideas or recommendations?
Thanks

Related

How to use ReactDOMServer.renderToStaticMarkup without nodejs server

I want to use ReactDOMServer.renderToStaticMarkup to create a static website, but I only want to generate the html string once during build instead of dynamically for every request
Here are the relevant docs, but they don't go into much detail on implementation https://reactjs.org/docs/react-dom-server.html#rendertostaticmarkup
What is the best practice for using react to generate html one time during build instead of dynamically on every page request? I am using webpack.
For anyone who comes here looking for answers, here's how I did it:
Create a separate webpack config file for your SSR react like webpack.ssr.js. This file will have the same config you'd normally have for react SSR.
In the index.js file where you'd usually have ReactDOM.render, run the ReactDOMServer.renderToStaticMarkup function.
In the index.js file, write the html string to a file:
const html = ReactDOMServer.renderToStaticMarkup(App);
fs.writeFile("index.html", html, (err) => {
if (err)
console.log(err);
else {
console.log("File written successfully\n");
}
});
After every build, execute the build file as a node script. For example node build.js. I'd recommend doing this in package.json as a postbuild script. For example:
postbuild: "node build.js"
The fs.writeFile will update the index.html file each time you build, so you'll just need to serve index.html like you would with any other static website.
For css modules, you'd use something like isomorphic-style-loader instead of the regular style-loader.
If you are using something like react-router, you'll need to iterate through all your routes and run ReactDOMServer.renderToStaticMarkup and fs.writeFile for every route. Then you'd serve the different html files with server side routing.
This will also work for normal SSR using ReactDOMServer.renderToString, especially if you don't need dynamic SSR based on something like an ID. For generating the loading screen server side, this is great and there's really no reason to generate html dynamically at runtime.

Is it possible to render handlebars templates from a database on node.js?

I'm looking to create a single node.js application that will render multiple different client websites.
We currently run one node.js application per client website, but I think this might be overkill as the serverside logic is exactly the same for all of them, the only difference being the handlebars template.
I'm looking to re-architecture to have a single node.js application which will then render the different client's websites based on some incoming information, will use nginx to add a header or something to the request so the app knows which website to render.
Is it possible to store the handlebars template within a database and then request the template at render time? Rendering a simple single page should be easy enough, but I'm struggling to understand how partials would be rendered?
Looking to achieve something similar to Shopify's Storefront Renderer, not sure if it's possible to do with handlebars or if it's better to use of of the LiquidJs ports for Node to achieve this?
https://shopify.engineering/how-shopify-reduced-storefront-response-times-rewrite
I do this with different countries, same concept as you with different Clients.
You just create the Handlebars layout and put it in your partials folder. No need to put template in your database.
Then your server logic will have eg clientName, and your Handlebars will have IfEqual tags (you'll need an IfEqual helper)
Example:
Server route will give a variable clientName.
Handlebars you have the main view page with only the IfEqual helpers.
{{#ifEqual clientName 'Client Name 1'}}
{{> client/clientName1}}
{{/ifEqual}}
{{#ifEqual clientName 'Client Name 2'}}
{{> client/clientName2}}
{{/ifEqual}}
Your helper function will be
, helpers: {
ifEqual: function(x, y, options) {
return(x == y) ? options.fn(this) : options.inverse(this)
} // {{#ifEqual statusLogin 'unconfirmed'}} {{/ifEqual}}
Search nodejs handlebar helper if you dont know how to set it.
So my partial folder, you can create a folder in it called client, and put all your Handlebars template in there. Search for nodejs handlebars partial folder setup if you dont know how to do it.
The handlebar helper function I found it I think in stack overflow, so you can search for others if you want, but for me it works.
Your partial template files just do as normal Handlebar files.
So the main idea is User loads the page, depending on what Client flag you put it, it loads up that template.

Node.js/Express render view from client side with javascript

I am working on NodeJS/Express project. Running it on localhost I am trying to render a view on the client-side. But I get file not found error.
The javascript is located in /public/js folder. The view i am trying to render in views/view1.ejs
I tried
window.location('/view1')
window.location.replace('/view1')
Getting 404 error Not Found in either case.
You need to provide the url(route) instead of the path of the file. The window.location.replace takes a url(resource) as a parameter not a file. Declare this inside your index.js route file.
router.get("/view1",function(req,res,next){
res.render("view1")
});

React isomorphic inconvenience

I'm building isomorphic app using Node.js and ReactJS.
I've met a situation when inside some isomorphic React component (rendered both on client and server sides) I need to include only client-side dependency (some notifications library for example).
What is the best way to have it worked (compiled) both on server/client sides?
P.S: I have few thoughts about possible workarounds something like checking for browser env variables (ex. some typeof window !== 'undefined') etc. but I'm not sure it's a good way to go.
Use the lifecycle method componentDidMount which is not invoked on the server instead of checking if window is undefined.
The "downside" is if your client side dependency enhances an element for example or changes any property of it, it'll get the default property first since it was rendered server side and when componentDidMount runs it'll get changed causing a "blink".
If you are using browserify I often use process.browser which is only true in browserified code.
If you wanted to get fancy and remove server code from the browser instead there is also isbrowser which will do just that.
Another way (webpack or browserify) is to take advantage of the browser field in the package.json. You can make it so that the server requires a noop file and the browser requires a file the exposes the client side api.
I have defined variable in webpack configuration file called process.env.WEBPACK and in my code when i need something like bottstrap js or something else i just write
if(process.env.WEBPACK){
//awesome lib included or scss or whatever
}

Rendering javascript assets in Node.js and Express

I have several environment settings that I need to inject into my javascript assets. Thinks like database uri and facebook app id. I'm currently using EJS as my view engine.
Is there a simple way of templating javascript files in NodeJS?
Rather than trying to template your static files, I'd suggest using an initializer script instead.
Simply put a <script> block in your layout, where you call a function in your JS file, which takes the DB uri and the FB app id as its parameters. The function can then store the values someplace where the rest of the scripts know how to access them.
You can easily pass your parameters into the layout (and views) for example by using helpers

Resources