javascript - Server side rendering with react, react-router, and express -
i'm trying set server-side rendering react app , i'm trying use great react-router module allow handle non-js situations (some crawlers, when user had js turned off reason). however, i'm running trouble. i've been using great response here https://stackoverflow.com/a/28558545/3314701 guide of sorts, i'm getting strange errors thrown @ me. persistent syntax error when trying use react.rendertostring(). setting server-side rendering incorrectly, missing obvious, or else?
my setup:
really basic express server
require('babel/register'); var app = express(); // misc. express config... var router = require('react-router'), routes = require('../jsx/app').routes, react = require('react'); app.use(function(req, res, next) { var router = router.create({location: req.url, routes: routes}); router.run(function(handler, state) { console.log(handler); var html = react.rendertostring(<handler/>); return res.render('react_page', {html: html}); }); }); top-level react <app/> component
// shims require('intl'); require('es5-shim'); var react = require('react/addons'), router = require('react-router'), nav = require('./nav'), injecttapeventplugin = require("react-tap-event-plugin"), window.react = react; // export http://fb.me/react-devtools // intl var reactintl = require('react-intl'), intlmixin = reactintl.intlmixin; var route = router.route, defaultroute = router.defaultroute, notfoundroute = router.notfoundroute, routehandler = router.routehandler; var app = react.createclass({ mixins: [intlmixin], getinitialstate: function() { return { connected: false, loaded: false, user: true }; }, render: function() { return ( <div classname="container-fluid"> <nav/> <routehandler/> <footer/> </div> ); } }); var routes = ( <route name="home" path="/" handler={app}> <defaultroute name="welcome " handler={welcome}/> <route name="bar" path="/bar" handler={bar}> <route name="foo" path="/foo" handler={foo}></route> </route> ); router.run(routes, router.historylocation , function(handler) { react.render(<handler/>, document.getelementbyid('app')); }); module.routes = routes; output:
flo-0,1,2 (err): <div classname="progressbar-container" > flo-0,1,2 (err): ^ flo-0,1,2 (err): syntaxerror: unexpected token < flo-0,1,2 (err): @ exports.runinthiscontext (vm.js:73:16) flo-0,1,2 (err): @ module._compile (module.js:443:25) flo-0,1,2 (err): @ module._extensions..js (module.js:478:10) flo-0,1,2 (err): @ object.require.extensions.(anonymous function) [as .js] (/users/user/code/foobar/apps/flo/node_modules/babel/node_modules/babel-core/lib/babel/api/register/node.js:161:7) flo-0,1,2 (err): @ module.load (module.js:355:32) flo-0,1,2 (err): @ function.module._load (module.js:310:12) flo-0,1,2 (err): @ function.<anonymous> (/users/user/.nvm/versions/node/v0.12.4/lib/node_modules/pm2/node_modules/pmx/lib/transaction.js:62:21) flo-0,1,2 (err): @ function.cls_wrapmethod (/users/user/code/foobar/apps/bar/node_modules/newrelic/lib/shimmer.js:230:38) flo-0,1,2 (err): @ function.<anonymous> (/users/user/code/foobar/apps/bar/node_modules/pmx/lib/transaction.js:62:21) flo-0,1,2 (err): @ module.require (module.js:365:17) flo-0,1,2 (err): @ require (module.js:384:17)
so, ended solving 1 myself. error getting un-rendered nested component, why js engine complaining random < char.
and express setup. aren't aware of how react can used server-side rendering, it's straightforward: node or io.js can used call react's rendertostring() method on component , sending requesting client. you've heard benefits approach brings already, don't know:
- you more seo-friendliness, though google can execute js in it's crawlers; pretty safer bet
- fallback non-js situations. if app script loading slowly, can still render actual page client , not make them wait while staring @ blank screen. allows js disabled on browser still interact app part; links still work, forms can still submit, &c.
- you can additional benefits of code-sharing between client , server. there's nothing incredible aside fact complexity decreased and, such, benefits of decreased complexity (potentially less coupling, easier maintainability, greater simplicity in structure, isomorphic-ness, &c.)
- a further side benefit ability use react-router's html5 history api instead of annoying hash-fragment stuff have otherwise use.
you crazy approach , handle things placeholders app while loads or provide other feedback mechanisms slow-loading state (a la facebook while loads).
the basic approach operates in following manner:
- upon bootstrap, node app instantiates react-router instance based on
routes.jsx - request goes server, uses express'
req.pathprovide route string react-router handle. - react router matches provided route , tries render corresponding component express send back.
- react sends down html response , client gets paint regardless of speed of app script. serve ours on great cdn, best distribution , compression slow networks still otherwise leave people temporarily blank screen.
- having loaded needed app script, react can use same
routes.jsxfile take on , generate htmlreact-routerhere on out. benefit here app code can cached , future interactions won't have rely on call.
one more point worth noting: use webpack bundle react code , browser.jsx entry point. before refactoring server-side rendering app.jsx; might need re-configure structure accommodate gets rendered where. :)
le code:
browser.jsx
const react = require('react'); const router = require('react-router').router; const hist = require('history'); const routes = require('./routes'); const newhistory = hist.createhistory(); react.render(<router history={newhistory}>{routes}</router>, window.document); app.js (express server):
//...other express configuration const routes = require('../jsx/routes'); const react = require('react'); const {routingcontext, match} = require('react-router'); const hist = require('history'); app.use((req, res, next) => { const location = hist.createlocation(req.path); match({ routes: routes, location: location, }, (err, redirectlocation, renderprops) => { if (redirectlocation) { res.redirect(301, redirectlocation.pathname + redirectlocation.search); } else if (err) { console.log(err); next(err); // res.send(500, error.message); } else if (renderprops === null) { res.status(404) .send('not found'); } else { res.send('<!doctype html>' + react.rendertostring(<routingcontext {...renderprops}/>)); } }); }); //...other express configuration routes.jsx
<route path="/" component={app}> <defaultroute component={welcome}/> <route path="dashboard" component={dashboard}/> <route path="login" component={login}/> </route> app.jsx
<html> <head> <link rel="stylesheet" href="/assets/styles/app.css"/> </head> <body> <navigation/> <routehandler/> <footer/> <body/> </html> helpful links:
Comments
Post a Comment