Skip to content

Instantly share code, notes, and snippets.

@simenbrekken
Created March 5, 2014 01:42
Show Gist options
  • Save simenbrekken/9359621 to your computer and use it in GitHub Desktop.
Save simenbrekken/9359621 to your computer and use it in GitHub Desktop.
React Async Server/Client Rendering
/** @jsx React.DOM */
'use strict';
var React = require('react'),
Router = require('./Router')
module.exports = React.createClass({
displayName: 'Application',
getInitialState: function() {
return {
match: Router.recognizePath(this.props.path || window.location.pathname)
}
},
navigate: function(path, callback) {
window.history.pushState(null, null, path)
this.setState({ match: Router.recognizePath(path) }, callback)
},
handleClick: function(e) {
if (e.target.tagName === 'A') {
e.preventDefault()
this.navigate(e.target.pathname)
}
},
handlePopstate: function() {
var path = window.location.pathname
if (this.state.match.path !== path) {
this.setState({ match: Router.recognizePath(path) })
}
},
componentDidMount: function() {
window.addEventListener('popstate', this.handlePopstate)
},
render: function() {
var Page = this.state.match.handler,
metadata = Page.getMetadata ? Page.getMetadata() : {},
title = metadata.title || 'Reactive',
description = metadata.description || 'Example react application'
return (
<html>
<head>
<title>{title}</title>
<meta name="description" content={description} />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta property="og:title" content={title} />
<meta property="og:type" content="website" />
<meta property="og:description" content={description} />
<meta property="og:image" content="/images/facebook-thumbnail.png" />
</head>
<body onClick={this.handleClick}>
<Page params={this.state.match.params} store={this.props.store} />
<script src="/client.js"></script>
</body>
</html>
)
}
})
/** @jsx React.DOM */
'use strict';
var React = require('react'),
Layout = require('./layout/Layout')
function fetchData(params, query) {
return {
motd: 'http://localhost:3000/api'
}
}
module.exports = React.createClass({
displayName: 'Dashboard',
getInitialState: function() {
return {
data: this.props.store.get(fetchData())
}
},
loadMissingData: function() {
if (!this.state.data) {
this.props.store.fetch(fetchData(), function(err, data) {
this.setState({ data: data })
}.bind(this))
}
},
componentWillMount: function() {
this.loadMissingData()
},
render: function() {
var motd
if (this.state.data) {
motd = <span className="motd">Message of the day: {this.state.data.motd.message}</span>
}
return (
<Layout>
<h1>Dashboard</h1>
{motd}
</Layout>
)
},
statics: {
fetchData: fetchData,
getMetadata: function() {
return {
title: 'Dashboard'
}
}
}
})
'use strict';
var express = require('express'),
path = require('path'),
browserify = require('connect-browserify'),
jsx = require('node-jsx')
jsx.install({extension: '.jsx'})
var app = module.exports = express(),
debug = app.get('env') == 'development'
// Mock API
app.get('/api', function(req, res) {
res.send({message: 'Hello from API'})
})
// Client bundler
app.get('/client.js', browserify({
entry: path.resolve('./client'),
extensions: ['.jsx', '.js', '.json'],
debug: debug,
watch: debug
}))
// Server rendering
// TODO: Router doesn't have to be global
var React = require('react'),
Router = require('./src/Router'),
Application = require('./src/Application'),
Store = require('./src/Store')
app.use(function(req, res, next) {
var path = req.path,
match = Router.recognizePath(path)
if (!match) return next()
// Initialize store
var store = new Store()
function send() {
var markup = React.renderComponentToString(Application({
path: path,
store: store
}))
// Inject store data into markup
markup = markup.replace('</head>', '<script>window._store = ' + store.toJSON() + '</script></head>')
res.send(markup)
}
// Load initial data
if (match.handler.fetchData) {
store.fetch(match.handler.fetchData(), function(err) {
if (err) return next(err)
send()
})
} else {
send()
}
})
if (!module.parent) {
var port = process.env.PORT || 3000
app.listen(port, function() {
console.log('Listening on port %s', port)
})
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment