I'm attempting to setup a simple app where I use React server rendering.
React 0.14.8, Express 4.13.4.
const React = require('react');
const ReactDOMServer = require('react-dom/server');
const express = require('express');
const exphbs = require('express-handlebars');
const myApp = React.createFactory(require('./app/components/app.jsx'));
// ...express/handlebars setup stuff here
app.get('/', function(req, res) {
const html = ReactDOMServer.renderToString(myApp());
res.render('home', { content: html });
});
My html variable contains the markup, however it's escaped and the browser shows the HTML.
I know about dangerouslySetInnerHTML(), however I really don't want to have to use that. It's named that for a reason.
So I'm left thinking that there must be something I'm missing here. As server rendering is quite a big feature of React, and dangerouslySetInnerHTML() is discouraged, the two don't really feel like they should be used together.
What am I missing..?
Can someone provide a really basic, bare-bones example of React server rendering..?
You're right, the only way to inject innerHtml when rendering, unfortunately, is by using that dreadfully named dangerouslySetInnerHTML() function. Basically it's asking you to really think about it before you do it. A nice example is written out here: https://camjackson.net/post/server-side-rendering-with-react
I think I was confused (it happens)... The HTML returned from renderToString() was actually ok, it was handlebars that was escaping the HTML.
In my Handlebars template file I changed {{content}} to {{{content}}} as per the docs: http://handlebarsjs.com/ (HTML Escaping).
The browser now renders the HTML as HTML instead of showing the HTML.
I feel I owe an apology to the ReactDOMServer.renderToString() method.
I'm sorry!
Related
I am working on node express server which generates reports (tables, charts) for data stored in database (MongoDB). What makes it hard is that the report should be downloadable as a single file and viewable offline. Which means that all the data and javascript should be inside the html file.
By searching stackoverflow I found this question - Inline CSS/Javascript into a HTML file. One of the solutions is command-line tool which visits the page and bundles together all assets, other solutions use other languages. In my case it doesn't make sense to visit the page and download the files as they are already in memory and should be imported from node server.
So here is example of how I've been doing it so far.
Server.js:
// Templating library
const mustache = require("mustache");
// Javascript to be imported into html as inline script
const someFunction = require("./src/someFunction.js").toString();
app.get("/:id", async (req, res) => {
// Get data from DB
const data = await MongooseSchema.find({ id: req.params.id });
// Read html view
const view = fs.readFileSync("./views/report.html", "utf8"});
// Render with mustache
const html = mustache.to_html(view, {
data,
javascript: { someFunction }
});
// Send rendered html
res.send(html);
});
And report.html includes this script tag:
<script>
const data = {{{data}}};
{{{javascript.someFunction}}}
</script>
I am importing javascript files as strings and then rendering them inside the html file using mustache templating library. This approach seems kind of hacky, there should be a better way.
Also I am thinking about implementing some bundler (webpack/parcel/rollup) to combine all the js files into one which I could then manually import into html as inline script.
And maybe even use some framework like react to make my life easier.
So my question is, is this approach viable? Are there any better ways to do this?
So title says it all. Never seen anyone use this syntax.
const app = require('express')()
I like to keep the main js file lean and have everything in modules so all I have there is 15 lines including comments.
Google didn't help and haven't found an answer here.
Is it bad practice to invoke express as you require it?
In general, your code should be the simplest and clearest way of reliably meeting your requirements. Any code that follows those simple guidelines will not be considered a bad practice in pretty much anyone's book.
There may be other influences on the desired coding style such as team style guidelines, coding style already present in the file/module/project and certain things you may want to do for testing, debugging or reuse. But, since you don't mention any of those influences, I will assume they are not present here.
So, with that first paragraph in mind, as long as you don't need access to the express module elsewhere in this module, then doing:
const app = require('express')();
is indeed the simplest and clearest way of accomplishing your goal and should not be considered a bad practice - in fact it should be considered a good practice.
On the other hand, if you were doing this:
const app = require('express')();
....
const mainRouter = require('express').Router();
....
const subRouter = require('express').Router();
....
app.use(require('express').static('public'));
Then, you'd have simpler and less redundant code (and perhaps a little faster since there are fewer function calls) if you loaded the express module into its own variable which you could then use everywhere else in that module:
const express = require('express');
const app = express();
....
const mainRouter = express.Router();
....
const subRouter = express.Router();
....
app.use(express.static('public'));
Something to consider is that the express module exposes some other functionality that you might want to make use of later (e.g. express.static). In your case you would have to require express again to gain access to it:
app.use(require('express').static());
Other than that there is no reason it's "bad practise". It just depends on what you intend to leverage from the module.
This is not a bad practise rather this is a short version of following code :
var express = require('express');
var app = express();
Simply put, to create an identifier for reuse and better communication to readers.
More thought just about require()
Since require() takes the responsibility to load and cache modules, some argues that it should be placed before app initialization. I see many example codes follows this style. However I think it really depends on how the package/code quality is ensured in the specific project.
I am working on an isomorphic javascript app with express + react. We started out using jade for server side templates for static content, but combining the two is quickly becoming unwieldy. We have ended up with something like this:
In the express routes:
router.get("/", function(req, res) {
var webpackStats = require('../../config/webpack-stats.json');
var reactHtml = React.renderToString(HiwApp({}));
var slideshowHtml = React.renderToString(slideshowApp({}));
var config = {
webpackStats: webpackStats,
reactOutput: reactHtml,
slideshowHtml: slideshowHtml
};
res.render("how_it_works/howitworks", config);
});
In Jade:
body
.company-logo.center
#react-main-mount
!= reactOutput
include ./content_block_1.jade
include ./content_block_2.jade
#slideshow-main-mount
!= slideshowHtml
This is very brittle-if we want jsx then a jade template then more jsx, we have to make sure we get the order right.
My idea is to do it all with jsx. I know there is React.renderToStaticMarkup for this sort of thing, but that doesn't solve the problem of mixing dynamic with static pages.
The big questions: if we decide to do all of this with jsx (say layout.jsx which contains all components), then call React.renderToString(App({});, will this be a major performance hit? If so, is there a better way to do it to easily combine static and dynamic blocks?
Although this may be a tiny bit off topic: We stuck with jade templates.
Basically we wanted the flexibility to use a non-react + flux architecture for areas of the site when and if that need arose. Our site is basically made up of a number of smaller SP apps: Site, UserAccount, Team and Admin.
Why did we do this?
Smaller filesize and overhead for users who are not accessing all sections of the site.
Option to "opt out" of React and flux if and when the need arises.
Simpler, server side authentication.
The way we have done it successfully was to render a JSX shell template (Html.jsx) on the server using React.renderToStaticMarkup() and then send it as the response to every server-side express route request that is meant to deliver some HTML to the browser. Html.jsx is just a shell containing html head information and GA scripts etc. It should contain no layout.
// Html.jsx
render(){
return (
<html>
<head>
// etc.
</head>
<body>
<div
id="app"
dangerouslySetInnerHTML={{__html: this.props.markup}}>
</div>
</body>
<script dangerouslySetInnerHTML={{__html: this.props.state}</script>
<script>
// GA Scripts etc.
</script>
</html>
)
}
Remember it is totally fine and even recommended to use dangerouslySetInnerHTML on the server when hydrating your app.
Dynamic layout should be done with your your isomorphic components through a hierarchy of components based on their state/props configuration. If you happen to be using React Router, then your router will render view handlers based on the routes you provide it so that means you don't need to manage that yourself.
The reason we use this technique is to architecturally separate our "App" which is isomorphic and responds to state from our server-side template shell which is just a delivery mechanism and is effectively boiler plate. We even keep the Html.jsx template amongst all the express components within our app and do not let it mix with the other isomorphic React components.
One of the most helpful resources I found for working out React/isomorphic architecture was https://github.com/yahoo/flux-examples/tree/master/react-router which is where we stole this technique from.
We explored the idea of integrating handlebars as a templating engine for client's devs using our products in the future but decided that it was less complex to write our own DSL in JSX and use some simple parsing routines to parse our HTML-like DSL to JSX by adding things like export default (ES6 module syntax) at the start of the template and then import the template to a rendering component.
You could of course follow that line of thought and use a jade compiler to spit out the template and then add module syntax around that if you think separate jade files are essential. I also noticed this project as well although I have not explored it in anger: https://github.com/jadejs/react-jade.
I have a Reactjs application built on a Node/Express server. I am doing server side rendering via something like:
route.js
var Group = require('./react/component/groups');
var props = {foo: 'bar'};
var groupHtml = React.renderToString(Group(props));
res.render('index.ejs', {
reactOutput: groupHtml,
jsonProps: JSON.stringify(props),
start: 'lessonlist'
});
index.ejs
<div id="react-main-mount"><%- reactOutput %></div>
<script id="props" type="application/json"><%- jsonProps %></script>
<script src="/js/<%= start %>.js"></script>
This works really great! But here is my problem:
One of my pages/components will have a child component. But this child component can be one of 50+ different components.
I assume I could Require every single component, even though only one will be used. This seems like a bad idea since my main.js file will be huge.
I tried sending it via a prop and JSON.stringify. This works on the server rendering to HTML, but this doesn't work on the client side because JSON.stringify can't stringify a react component.
I though I might be able to do an API call and return it, but same problem as above.
Is there any way to dynamically require something, say based off a variable like require('components/' + this.props.foo) after the I've browserified/reactified?
Any suggestions/answers would be greatly appreciated
Browserify had always been too bulky for me, so I did my research and found RequireJS. It allowed me to require other files on the client just like how Node does it.
It was difficult for me to get started with Require, but once I got it working, everything just seemed easier. See this example I wrote in CoffeeScript.
Remember to have below code at the top of every isomorphic component so you don't stumble upon the problem I had with Node not understanding the keyword define.
if typeof define != 'function'
define = require('amdefine')(module)
define (require) ->
Above code is written in coffeescript.
Let me know if more information is needed.
Webpack allows you to do asynchronous bundle loading. This is intended for single page applications, where you don't want to have to include all of your javascript files until the user actually navigates to the "page" where they're needed. This allows you to fetch javascript with ajax dynamically. Check out the section titled 9. Async loading in this wonderful guide to Webpack
The implementation looks something like this...
if (window.location.pathname === '/feed') {
showLoadingState();
require.ensure([], function() { // this syntax is weird but it works
hideLoadingState();
require('./feed').show(); // when this function is called, the module is guaranteed to be synchronously available.
});
} else if (window.location.pathname === '/profile') {
showLoadingState();
require.ensure([], function() {
hideLoadingState();
require('./profile').show();
});
}
If you don't want to use Webpack, you could potentially just fetch the components with ajax as plain text and eval it upon arrival. This is essentially what the browser is doing with script tags under the hood.
Is it okay for a site to use Express for handling the routing when using Server side rendered React templates rather than React Router?
I am looking to use Fluxxor similar to this example for my components and stores/actions. Then I was planning to use Express router to handle the page routing server side, and on each route render the desired component to string. In this basic example below my app.js is a simple todo example using Flux but in production it would likely be a top level component appropriate for the routed page. e.g. /products would be a product component, with its subcomponents.
Here is my basic server.js file
var express = require('express');
var React = require('react');
var App = require('./app');
var app = express();
var port = 4580;
// include my browserify bundle.js
app.use('/public', express.static(__dirname + '/public'));
app.get('/', function(req, res) {
/* each route will render the particular component to string. */
var markup = React.renderToString(App());
res.send(markup);
});
// I will seperate my views out like this
// app.get('/products', product.list);
// app.get('/users', user.list);
// app.get('/products:id', product.list);
My basic server side render approach is here if that helps.
So as I was saying, Is it okay to use Express for the routing, as I am finding React Rouuter to be quite complex, and it just seems easier to have routes on the server, and to render each page with its components server-side.
Am I losing anything else here other than the complexity of handling the routing in Express rather than React Router? As I think I can handle that, but want to make sure I haven't messed up the point of server side rendering with React by just using Express to render to string based on different routes.
Just to be clear, I'm okay with the issues which React Router aims to solve, and understand that I will be doing something similar to what was suggested in the React Router overview as being problematic in the long run.
// Not React Router API
otherRouter.route('/', function () {
React.render(<DashboardRoute/>, document.body);
});
otherRouter.route('/inbox', function () {
React.render(<InboxRoute/>, document.body);
});
otherRouter.route('/calendar', function () {
React.render(<CalendarRoute/>, document.body);
});
While one level of shared UI like this is pretty easy to handle, getting deeper and deeper adds more complexity.
I am basically assuming that I can handle this complexity okay, whether that is true or not, I guess I will find out in time.
Sounds like you are trying to make your website isomorphic—rendering static markup on the server, attaching event listeners on the client.
The approach I use for my projects is embedding client-side javascript in the server-rendered string, so that the same props are passed down and re-rendering is not required on the client.
It seems to be very complicated to explain with only a few snippets of code, so check out this template I created for this purpose. There's also a tutorial about this topic here.