Permalink
Browse files

Using routes

  • Loading branch information...
1 parent cffce64 commit 29a099f1ea1305cf3ec4d20392907c41fc78aa32 @alexishevia committed Nov 27, 2014
Showing with 278 additions and 9 deletions.
  1. +22 −1 README.md
  2. +16 −0 app.js
  3. +8 −0 components/About.jsx
  4. +14 −1 components/Application.jsx
  5. +8 −0 components/Home.jsx
  6. +12 −0 configs/routes.js
  7. +123 −0 npm-shrinkwrap.json
  8. +4 −1 package.json
  9. +26 −6 server.js
  10. +45 −0 stores/ApplicationStore.js
View
@@ -1,8 +1,29 @@
# Fluxible-App Step by Step
-On this post I want to show how to create a fluxible-app step by step. I will be developing one (or several) of Yahoo's [flux examples](https://github.com/yahoo/flux-examples).
+On this post I want to show how to create a fluxible app step by step. I will be developing one (or several) of Yahoo's [flux examples](https://github.com/yahoo/flux-examples).
## Hello World
- Install node.js and npm
- package.json with initial dependencies
- create a basic React component that just renders 'Hello World'
- create an express app (server.js). Add a middleware to render our React component.
+
+## Using routes
+- Create <Home> and <About> Components
+- Modify the ApplicationComponent so it renders either <Home> or <About> based on the current route.
+- Create app.js, our Fluxible-app. app.js uses a routrPlugin (with our custom defined routes), and an ApplicationStore, which holds the current route as its state.
+- Modify server.js so it creates a new context on each request, and executes the NavigateAction before rendering.
+
+This is the process:
+1. A new request is made, server.js receives it.
+2. Our middleware creates a new context instance and calls context.executeAction(navigateAction), passing it the current route.
+3. NavigateAction uses the routrPlugin to look for a matching route.
+ 3.1 If a match is found, a 'CHANGE_ROUTE_SUCCESS' action is dispatched and the callback is called with no errors.
+ 3.2 If a match is not found, the callback is called with a 'Url does not exist' error.
+4. The 'CHANGE_ROUTE_SUCCESS' action is dispatched to all stores registered with the app (ApplicationStore in our case).
+Note: A new ApplicationStore instance is created, and the action is immediately dispatched to it.
+5. ApplicationStore executes its handleNavigate() method in response to the 'CHANGE_ROUTE_SUCCESS' action. The handleNavigate() method will update the ApplicationStore state.
+6. Inside the executeAction callback, we'll create a new instance of our Application component, passing it the current context as a prop. The context, among other things, contains the ApplicationStore instance that was created on step 4.
+7. We render the Application component as a string, and send the result as our response.
+Since the Application component gets its state from ApplicationStore, and ApplicationStore was updated when it handled the 'CHANGE_ROUTE_SUCCESS', we can use the current state to determine which sub-component to render (<Home> or <About>)
+
+Note: We're using the FluxibleMixin, which includes a handy `getStore()` method that knows how to get the correct store instance from the provided context.
View
@@ -0,0 +1,16 @@
+'use strict';
+var React = require('react');
+var Fluxible = require('fluxible');
+var routrPlugin = require('fluxible-plugin-routr');
+
+var app = new Fluxible({
+ appComponent: React.createFactory(require('./components/Application.jsx'))
+});
+
+app.plug(routrPlugin({
+ routes: require('./configs/routes')
+}));
+
+app.registerStore(require('./stores/ApplicationStore'));
+
+module.exports = app;
@@ -0,0 +1,8 @@
+'use strict';
+var React = require('react');
+
+module.exports = React.createClass({
+ render: function(){
+ return <div>This is a description of the site.</div>;
+ }
+});
@@ -1,8 +1,21 @@
+'use strict';
var React = require('react');
+var ApplicationStore = require('../stores/ApplicationStore');
+var Home = require('./Home.jsx');
+var About = require('./About.jsx');
+var FluxibleMixin = require('fluxible').Mixin;
var Application = React.createClass({
+ mixins: [FluxibleMixin],
+ getInitialState: function () {
+ return this.getStore(ApplicationStore).getState();
+ },
render: function(){
- return <div>Hello World!</div>;
+ return (
+ <div>
+ {'home' === this.state.currentPageName ? <Home/> : <About/>}
+ </div>
+ );
}
});
View
@@ -0,0 +1,8 @@
+'use strict';
+var React = require('react');
+
+module.exports = React.createClass({
+ render: function(){
+ return <div>Welcome to the site!</div>;
+ }
+});
View
@@ -0,0 +1,12 @@
+module.exports = {
+ home: {
+ path: '/',
+ method: 'get',
+ page: 'home'
+ },
+ about: {
+ path: '/about',
+ method: 'get',
+ page: 'about'
+ }
+};
View
@@ -212,6 +212,129 @@
}
}
},
+ "flux-router-component": {
+ "version": "0.4.6",
+ "from": "flux-router-component@>=0.4.0 <0.5.0",
+ "resolved": "https://registry.npmjs.org/flux-router-component/-/flux-router-component-0.4.6.tgz",
+ "dependencies": {
+ "debug": {
+ "version": "2.1.1",
+ "from": "debug@>=2.0.0 <3.0.0",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-2.1.1.tgz",
+ "dependencies": {
+ "ms": {
+ "version": "0.6.2",
+ "from": "ms@0.6.2",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-0.6.2.tgz"
+ }
+ }
+ },
+ "object-assign": {
+ "version": "2.0.0",
+ "from": "object-assign@>=2.0.0 <3.0.0",
+ "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-2.0.0.tgz"
+ },
+ "query-string": {
+ "version": "1.0.0",
+ "from": "query-string@>=1.0.0 <2.0.0",
+ "resolved": "https://registry.npmjs.org/query-string/-/query-string-1.0.0.tgz"
+ },
+ "setimmediate": {
+ "version": "1.0.2",
+ "from": "setimmediate@>=1.0.2 <2.0.0",
+ "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.2.tgz"
+ }
+ }
+ },
+ "fluxible": {
+ "version": "0.2.0",
+ "from": "fluxible@*",
+ "resolved": "https://registry.npmjs.org/fluxible/-/fluxible-0.2.0.tgz",
+ "dependencies": {
+ "async": {
+ "version": "0.9.0",
+ "from": "async@>=0.9.0 <0.10.0",
+ "resolved": "https://registry.npmjs.org/async/-/async-0.9.0.tgz"
+ },
+ "debug": {
+ "version": "2.1.1",
+ "from": "debug@>=2.0.0 <3.0.0",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-2.1.1.tgz",
+ "dependencies": {
+ "ms": {
+ "version": "0.6.2",
+ "from": "ms@0.6.2",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-0.6.2.tgz"
+ }
+ }
+ },
+ "dispatchr": {
+ "version": "0.2.12",
+ "from": "dispatchr@>=0.2.5 <0.3.0",
+ "resolved": "https://registry.npmjs.org/dispatchr/-/dispatchr-0.2.12.tgz"
+ },
+ "object-assign": {
+ "version": "2.0.0",
+ "from": "object-assign@>=2.0.0 <3.0.0",
+ "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-2.0.0.tgz"
+ },
+ "setimmediate": {
+ "version": "1.0.2",
+ "from": "setimmediate@>=1.0.2 <2.0.0",
+ "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.2.tgz"
+ }
+ }
+ },
+ "fluxible-plugin-routr": {
+ "version": "0.2.1",
+ "from": "fluxible-plugin-routr@>=0.2.0 <0.3.0",
+ "resolved": "https://registry.npmjs.org/fluxible-plugin-routr/-/fluxible-plugin-routr-0.2.1.tgz",
+ "dependencies": {
+ "debug": {
+ "version": "2.1.1",
+ "from": "debug@>=2.0.0 <3.0.0",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-2.1.1.tgz",
+ "dependencies": {
+ "ms": {
+ "version": "0.6.2",
+ "from": "ms@0.6.2",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-0.6.2.tgz"
+ }
+ }
+ },
+ "routr": {
+ "version": "0.0.5",
+ "from": "routr@>=0.0.0 <0.1.0",
+ "resolved": "https://registry.npmjs.org/routr/-/routr-0.0.5.tgz",
+ "dependencies": {
+ "path-to-regexp": {
+ "version": "1.0.3",
+ "from": "path-to-regexp@>=1.0.0 <2.0.0",
+ "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.0.3.tgz",
+ "dependencies": {
+ "isarray": {
+ "version": "0.0.1",
+ "from": "isarray@0.0.1",
+ "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz"
+ }
+ }
+ },
+ "reverend": {
+ "version": "0.3.0",
+ "from": "reverend@>=0.3.0 <0.4.0",
+ "resolved": "https://registry.npmjs.org/reverend/-/reverend-0.3.0.tgz",
+ "dependencies": {
+ "path-to-regexp": {
+ "version": "0.2.5",
+ "from": "path-to-regexp@>=0.2.1 <0.3.0",
+ "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.2.5.tgz"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
"node-jsx": {
"version": "0.12.4",
"from": "node-jsx@>=0.12.0 <0.13.0",
View
@@ -8,7 +8,10 @@
"dependencies": {
"express": "4.11.2",
"node-jsx": "0.12.4",
- "react": "0.12.2"
+ "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"
View
@@ -1,15 +1,35 @@
+'use strict';
+require('node-jsx').install({ extension: '.jsx' });
var express = require('express');
var server = express();
var port = process.env.PORT || 3000;
-
-require('node-jsx').install({ extension: '.jsx' });
+var navigateAction = require('flux-router-component').navigateAction;
var React = require('react');
-var AppComponent = React.createFactory(require('./components/Application.jsx'));
+var app = require('./app');
server.use(function(req, res, next) {
- var component = AppComponent();
- var html = React.renderToString(component);
- res.send(html);
+ var context = app.createContext();
+
+ context.getActionContext().executeAction(navigateAction, {
+ path: req.path
+ }, function (err) {
+ if (err) {
+ if (err.status && err.status === 404) {
+ next();
+ } else {
+ next(err);
+ }
+ return;
+ }
+
+ var AppComponent = app.getAppComponent();
+ var component = AppComponent({
+ context: context.getComponentContext()
+ });
+ var html = React.renderToString(component);
+
+ res.send(html);
+ });
});
server.listen(port);
@@ -0,0 +1,45 @@
+/**
+ * Copyright 2014, Yahoo! Inc.
+ * Copyrights licensed under the New BSD License. See the accompanying LICENSE file for terms.
+ */
+'use strict';
+var createStore = require('fluxible/utils/createStore');
+
+var ApplicationStore = createStore({
+ storeName: 'ApplicationStore',
+ handlers: {
+ 'CHANGE_ROUTE_SUCCESS': 'handleNavigate'
+ },
+ initialize: function (dispatcher) {
+ this.currentPageName = null;
+ this.currentPage = null;
+ this.currentRoute = null;
+ this.pages = require('../configs/routes');
+ },
+ handleNavigate: function (route) {
+ var pageName = route.name;
+ var page = this.pages[pageName];
+
+ if (pageName === this.getCurrentPageName()) {
+ return;
+ }
+
+ this.currentPageName = pageName;
+ this.currentPage = page;
+ this.currentRoute = route;
+ this.emitChange();
+ },
+ getCurrentPageName: function () {
+ return this.currentPageName;
+ },
+ getState: function () {
+ return {
+ currentPageName: this.currentPageName,
+ currentPage: this.currentPage,
+ pages: this.pages,
+ route: this.currentRoute
+ };
+ }
+});
+
+module.exports = ApplicationStore;

0 comments on commit 29a099f

Please sign in to comment.