12. Improved Server Rendering
My first attempt to clean up the server rendering resulted in the following index.jsx
file.
But be warned: this didn’t work
var React = require('react') , HelloWorld = require('./Components/HelloWorld') , express = require('express') , path = require('path') var app = express() app.use('/Components', express.static(path.join(path.join(__dirname, '..'), 'Components'))) app.get('/', function (req, res) { res.writeHead(200, {'Content-Type': 'text/html'}) var elementCode = 'var timestampElement = React.render( timestampInstance, document.getElementById(\'reactContainer\'))' var html = React.renderToString( <html> <head> <title>Hello World</title> <script src="//fb.me/react-0.13.1.js"></script> <script src="/Components/Timestamp.js"></script> </head> <body> <HelloWorld from="index.jsx on the server" /> <div id="reactContainer" /> </body> <script> var timestampInstance = React.createFactory(Timestamp)(); var timestampElement = React.render(timestampInstance, document.getElementById("reactContainer")); setInterval(function() { timestampElement.setState({ date: "Updated through setState: " + new Date().toString() }) }, 500) </script> </html>) res.end(html) }) app.listen(1337) console.log('Server running at http://localhost:1337/')
Trying to run this results in a broken page and an error visible on the console:
ReferenceError: timestampElement is not defined
This confused me for a while, but after some investigation (removing some of the inline script and building it back up), I found that the JSX parser is trying to process the contents of the <script>
tag. When the setInterval
statement is evaluated, the { timestampElement.setState...
is actually processed on the server!
Additionally, I saw that the document.getElementById("reactContainer")
statement’s quotes were also getting escaped by the rendering, and I couldn’t find a straight-forward day to address that.
Ugh - so I’ve now learned that combining inline <script>
tags and JSX is not a good recipe. We’ll need a different approach. We’ll go with a simple solution for the moment and just extract that code out into a separate JS file–one specifically for this page.
Let’s create a top-level assets
folder and create this file as assets/index.js
.
var timestampInstance = React.createFactory(Timestamp)(); var timestampElement = React.render(timestampInstance, document.getElementById("reactContainer")); setInterval(function() { timestampElement.setState({ date: "Updated through setState: " + new Date().toString() }) }, 500)
Then we’ll update our index.jsx
file for Express to also serve static assets from our assets
folder, and then we’ll change our inline <script>
tag over to <script src="/assets/index.js"></script>
.
var React = require('react') , HelloWorld = require('./Components/HelloWorld') , express = require('express') , path = require('path') var app = express() app.use('/Components', express.static(path.join(path.join(__dirname, '..'), 'Components'))) app.use('/assets', express.static(path.join(path.join(__dirname, '..'), 'assets'))) app.get('/', function (req, res) { res.writeHead(200, {'Content-Type': 'text/html'}) var html = React.renderToString( <html> <head> <title>Hello World</title> <script src="//fb.me/react-0.13.1.js"></script> <script src="/Components/Timestamp.js"></script> </head> <body> <HelloWorld from="index.jsx on the server" /> <div id="reactContainer" /> </body> <script src="/assets/index.js"></script> </html>) res.end(html) }) app.listen(1337) console.log('Server running at http://localhost:1337/')
Now the page is working again!