Permalink
Browse files

Client App

  • Loading branch information...
1 parent 1e08f68 commit b7b6f4b17cd3c4165cfc4a313860d97a412b7078 @alexishevia committed Nov 27, 2014
Showing with 85 additions and 6 deletions.
  1. +9 −0 README.md
  2. +17 −0 client.js
  3. +10 −2 components/Application.jsx
  4. +2 −0 components/HTML.jsx
  5. +5 −0 npm-shrinkwrap.json
  6. +5 −2 package.json
  7. +10 −2 server.js
  8. +9 −0 stores/ApplicationStore.js
  9. +18 −0 webpack.config.js
View
@@ -35,3 +35,12 @@ Note: We're using the FluxibleMixin, which includes a handy `getStore()` method
// To get styling
- Create an `<HTML>` component, to render the base template for our app (including styles)
- Modify server.js so it renders the app component inside the html component
+
+## Client App
+- Add dehydrate and rehydrate functions to our ApplicationStore, to control how its serialized/deserialized.
+- Use express-state to expose current state to `res.local.state`, inside an `App` namespace. Current state is obtained by calling app.dehydrate(), which in turn calls dehydrate() on all stores registered with the app.
+- Modify the `<HTML>` component so it renders res.local.state.
+- Create client.js. It will create a new app instance, and rehydrate it with the state passed from the server.
+- Use webpack to compile/bundle the client. Modify `<HTML>` so it loads the bundled client.
+- Add a store listener on `<Application>` so it updates whenever the ApplicationStore changes (eg: when the user clicks on a link and the current route changes)
+- Add the RouterMixin to `<Application>` so the browser URL is updated correctly when the user visits a new route.
View
@@ -0,0 +1,17 @@
+'use strict';
+var React = require('react');
+var app = require('./app');
+var dehydratedState = window.App; // Sent from the server
+
+window.React = React; // For chrome dev tool support
+
+app.rehydrate(dehydratedState, function (err, context) {
+ if (err) {
+ throw err;
+ }
+ var mountNode = document.getElementById('app');
+
+ React.render(app.getAppComponent()({
+ context: context.getComponentContext()
+ }), mountNode);
+});
@@ -1,16 +1,24 @@
'use strict';
var React = require('react');
-var ApplicationStore = require('../stores/ApplicationStore');
var Home = require('./Home.jsx');
var About = require('./About.jsx');
var Nav = require('./Nav.jsx');
+var ApplicationStore = require('../stores/ApplicationStore');
+var RouterMixin = require('flux-router-component').RouterMixin;
var FluxibleMixin = require('fluxible').Mixin;
var Application = React.createClass({
- mixins: [FluxibleMixin],
+ mixins: [RouterMixin, FluxibleMixin],
+ statics: {
+ storeListeners: [ApplicationStore]
+ },
getInitialState: function () {
return this.getStore(ApplicationStore).getState();
},
+ onChange: function () {
+ var state = this.getStore(ApplicationStore).getState();
+ this.setState(state);
+ },
render: function(){
return (
<div>
View
@@ -14,6 +14,8 @@ module.exports = React.createClass({
<body>
<div id="app" dangerouslySetInnerHTML={{__html: this.props.markup}}></div>
</body>
+ <script dangerouslySetInnerHTML={{__html: this.props.state}}></script>
+ <script src="/public/js/client.js" defer></script>
</html>
);
}
View
@@ -212,6 +212,11 @@
}
}
},
+ "express-state": {
+ "version": "1.2.0",
+ "from": "express-state@>=1.2.0 <2.0.0",
+ "resolved": "https://registry.npmjs.org/express-state/-/express-state-1.2.0.tgz"
+ },
"flux-router-component": {
"version": "0.4.6",
"from": "flux-router-component@>=0.4.0 <0.5.0",
View
@@ -3,17 +3,20 @@
"private": true,
"version": "0.0.1",
"scripts": {
- "dev": "./node_modules/.bin/nodemon -e js,jsx server.js"
+ "dev": "./node_modules/.bin/nodemon --ignore build/ -e js,jsx server.js & ./node_modules/.bin/webpack --watch"
},
"dependencies": {
"express": "4.11.2",
+ "express-state": "1.2.0",
"node-jsx": "0.12.4",
"react": "0.12.2",
"flux-router-component": "0.4.6",
"fluxible": "0.2.0",
"fluxible-plugin-routr": "0.2.1"
},
"devDependencies": {
- "nodemon": "1.3.6"
+ "nodemon": "1.3.6",
+ "webpack": "1.5.3",
+ "jsx-loader": "0.12.2"
}
}
View
@@ -1,13 +1,17 @@
'use strict';
require('node-jsx').install({ extension: '.jsx' });
var express = require('express');
-var server = express();
-var port = process.env.PORT || 3000;
var navigateAction = require('flux-router-component').navigateAction;
var React = require('react');
var app = require('./app');
var HtmlComponent = React.createFactory(require('./components/HTML.jsx'));
+var server = express();
+server.use('/public', express.static(__dirname + '/build'));
+
+var expressState = require('express-state');
+expressState.extend(server);
+
server.use(function(req, res, next) {
var context = app.createContext();
@@ -23,8 +27,11 @@ server.use(function(req, res, next) {
return;
}
+ res.expose(app.dehydrate(context), 'App');
+
var AppComponent = app.getAppComponent();
var html = React.renderToStaticMarkup(HtmlComponent({
+ state: res.locals.state,
markup: React.renderToString(AppComponent({
context: context.getComponentContext()
}))
@@ -34,5 +41,6 @@ server.use(function(req, res, next) {
});
});
+var port = process.env.PORT || 3000;
server.listen(port);
console.log('Listening on port ' + port);
@@ -39,6 +39,15 @@ var ApplicationStore = createStore({
pages: this.pages,
route: this.currentRoute
};
+ },
+ dehydrate: function () {
+ return this.getState();
+ },
+ rehydrate: function (state) {
+ this.currentPageName = state.currentPageName;
+ this.currentPage = state.currentPage;
+ this.pages = state.pages;
+ this.currentRoute = state.route;
}
});
View
@@ -0,0 +1,18 @@
+'use strict';
+var webpack = require('webpack');
+
+module.exports = {
+ resolve: {
+ extensions: ['', '.js', '.jsx']
+ },
+ entry: './client.js',
+ output: {
+ path: __dirname+'/build/js',
+ filename: 'client.js'
+ },
+ module: {
+ loaders: [
+ { test: /\.jsx$/, loader: 'jsx-loader' }
+ ]
+ }
+};

0 comments on commit b7b6f4b

Please sign in to comment.