commit 7580590064a8c1128a2a3486da90dd9d6baced86
parent aabbd38b63088c7bb8c24503fb85c0c858fed245
Author: Konstantin Tarkus <hello@tarkus.me>
Date: Wed, 1 Oct 2014 02:02:41 +0400
Update routing, implement Flux architecture. Closes #14
Diffstat:
15 files changed, 221 insertions(+), 100 deletions(-)
diff --git a/README.md b/README.md
@@ -24,16 +24,18 @@
├── /docs/ # Documentation files
├── /node_modules/ # Node.js-based dev tools and utilities
├── /src/ # The source code of the application
+│ ├── /actions/ # Action methods that allow to trigger a dispatch to stores
│ ├── /assets/ # Static files which don't require pre-processing
-│ ├── /data/ # Data access layer and models
-│ ├── /common/ # Utility classes etc.
+│ ├── /constants/ # Enumerations used in action methods and stores
│ ├── /components/ # React components. E.g. Navbar.jsx
│ ├── /images/ # Graphics (.png, .jpg, .svg etc.)
│ ├── /layouts/ # Layouts for web pages
│ ├── /pages/ # Web pages. E.g. Profile.jsx (or .html, .jade etc.)
│ ├── /services/ # Services and business logic
+│ ├── /stores/ # Stores contain the application state and logic
│ ├── /styles/ # LESS style sheets (or SASS/SCSS, Stylus etc.)
-│ └── /app.jsx # Entry point of your web application
+│ ├── /App.jsx # Entry point of your web application
+│ └── /AppDispatcher.js # The central hub that manages all data flow
├── /test/ # Unit, integration and load tests
│ ├── /e2e/ # End-to-end tests
│ └── /unit/ # Unit tests
diff --git a/config/webpack.js b/config/webpack.js
@@ -73,7 +73,7 @@ module.exports = function (release) {
loader: 'url-loader?limit=10000&mimetype=image/png'
},
{
- test: /\.jsx$/,
+ test: /\.(js|jsx)$/,
loader: 'jsx-loader?harmony'
}
]
diff --git a/gulpfile.js b/gulpfile.js
@@ -77,10 +77,10 @@ gulp.task('images', function () {
src.images = 'src/images/**';
return gulp.src(src.images)
.pipe($.changed(DEST + '/images'))
- .pipe($.cache($.imagemin({
+ .pipe($.imagemin({
progressive: true,
interlaced: true
- })))
+ }))
.pipe(gulp.dest(DEST + '/images'))
.pipe($.size({title: 'images'}));
});
diff --git a/package.json b/package.json
@@ -7,8 +7,9 @@
"license": "MIT",
"dependencies": {
"bootstrap": "^3.2.0",
- "react": "^0.11.2",
- "react-router": "^0.7.0"
+ "director": "^1.2.3",
+ "flux": "^2.0.1",
+ "react": "^0.11.2"
},
"devDependencies": {
"browser-sync": "^1.5.1",
diff --git a/src/App.jsx b/src/App.jsx
@@ -1,21 +1,33 @@
-/**
- * @jsx React.DOM
- */
-
'use strict';
var React = require('react');
-var {Routes, Route} = require('react-router');
+var {Router} = require('director');
+var AppDispatcher = require('./AppDispatcher');
+var ActionTypes = require('./constants/ActionTypes');
// Export React so the dev tools can find it
(window !== window.top ? window.top : window).React = React;
-React.renderComponent(
- <Routes location="history">
- <Route name="app" path="/" handler={require('./layouts/Default.jsx')}>
- <Route name="home" path="/" handler={require('./pages/Home.jsx')} />
- <Route name="privacy" handler={require('./pages/Privacy.jsx')} />
- </Route>
- </Routes>,
- document.body
-);
+function render(component) {
+ React.renderComponent(component(), document.body);
+}
+
+var routes = {
+ '/': () => render(require('./pages/Home.jsx')),
+ '/privacy': () => render(require('./pages/Privacy.jsx'))
+};
+
+var router = new Router(routes).configure({html5history: true}).init();
+
+AppDispatcher.register(function(payload) {
+
+ var action = payload.action;
+
+ switch (action.actionType) {
+ case ActionTypes.SET_CURRENT_ROUTE:
+ router.setRoute(action.route);
+ break;
+ }
+
+ return true; // No errors. Needed by promise in Dispatcher.
+});
diff --git a/src/AppDispatcher.js b/src/AppDispatcher.js
@@ -0,0 +1,39 @@
+/*
+ * A singleton that operates as the central hub for application updates.
+ */
+
+'use strict';
+
+var {Dispatcher} = require('flux');
+var PayloadSources = require('./constants/PayloadSources');
+var copyProperties = require('react/lib/copyProperties');
+
+var AppDispatcher = copyProperties(new Dispatcher(), {
+
+ /**
+ * @param {object} action The details of the action, including the action's
+ * type and additional data coming from the server.
+ */
+ handleServerAction: function(action) {
+ var payload = {
+ source: PayloadSources.SERVER_ACTION,
+ action: action
+ };
+ this.dispatch(payload);
+ },
+
+ /**
+ * @param {object} action The details of the action, including the action's
+ * type and additional data coming from the view.
+ */
+ handleViewAction: function(action) {
+ var payload = {
+ source: PayloadSources.VIEW_ACTION,
+ action: action
+ };
+ this.dispatch(payload);
+ }
+
+});
+
+module.exports = AppDispatcher;
diff --git a/src/actions/RouteActions.js b/src/actions/RouteActions.js
@@ -0,0 +1,21 @@
+'use strict';
+
+var AppDispatcher = require('../AppDispatcher');
+var ActionTypes = require('../constants/ActionTypes');
+
+var AppActions = {
+
+ /**
+ * Set the current route.
+ * @param {string} route Supply a route value, such as `todos/completed`.
+ */
+ setRoute: function(route) {
+ AppDispatcher.handleViewAction({
+ actionType: ActionTypes.SET_CURRENT_ROUTE,
+ route: route
+ });
+ }
+
+};
+
+module.exports = AppActions;
diff --git a/src/components/Link.jsx b/src/components/Link.jsx
@@ -0,0 +1,30 @@
+/**
+ * @jsx React.DOM
+ */
+
+'use strict';
+
+var React = require('react');
+var RouteActions = require('../actions/RouteActions');
+
+var Link = React.createClass({
+ propTypes: {
+ to: React.PropTypes.string.isRequired,
+ children: React.PropTypes.component.isRequired
+ },
+ render() {
+ this.props.href =
+ this.props.to && this.props.to.lastIndexOf('/', 0) === 0 ?
+ this.props.to : '/' + this.props.to;
+
+ return this.transferPropsTo(
+ <a onClick={this.handleClick}>{this.props.children}</a>
+ );
+ },
+ handleClick(e) {
+ e.preventDefault();
+ RouteActions.setRoute(this.props.to);
+ }
+});
+
+module.exports = Link;
diff --git a/src/components/Navbar.jsx b/src/components/Navbar.jsx
@@ -5,14 +5,14 @@
'use strict';
var React = require('react');
-var {Link} = require('react-router');
+var Link = require('./Link.jsx');
var Navbar = React.createClass({
render() {
return (
<div className="navbar-top">
<div className="container">
- <Link className="navbar-brand row" to="home">
+ <Link className="navbar-brand row" to="/">
<img src="/images/logo-small.png" width="38" height="38" alt="React" />
{' Facebook React Starter Kit'}
</Link>
diff --git a/src/components/NavbarSpec.jsx b/src/components/NavbarSpec.jsx
@@ -5,7 +5,7 @@ describe('Navbar', () => {
beforeEach(() => {
Navbar = require('./Navbar.jsx');
- component = new Navbar();
+ component = Navbar();
});
it('should create a new instance of Navbar', () => {
diff --git a/src/constants/ActionTypes.js b/src/constants/ActionTypes.js
@@ -0,0 +1,6 @@
+module.exports = {
+
+ // Route action types
+ SET_CURRENT_ROUTE: 'SET_CURRENT_ROUTE'
+
+};
diff --git a/src/constants/PayloadSources.js b/src/constants/PayloadSources.js
@@ -0,0 +1,4 @@
+module.exports = {
+ VIEW_ACTION: 'VIEW_ACTION',
+ SERVER_ACTION: 'SERVER_ACTION'
+};
diff --git a/src/layouts/Default.jsx b/src/layouts/Default.jsx
@@ -5,7 +5,7 @@
'use strict';
var React = require('react');
-var {Link} = require('react-router');
+var Link = require('../components/Link.jsx');
var Navbar = require('../components/Navbar.jsx');
var DefaultLayout = React.createClass({
@@ -19,13 +19,13 @@ var DefaultLayout = React.createClass({
<p>Complex web apps made easy</p>
</div>
</div>
- <this.props.activeRouteHandler />
+ {this.props.children}
<div className="navbar-footer">
<div className="container">
<p className="text-muted">
{' © KriaSoft • '}
- <Link to="home">Home</Link> {' • '}
- <Link to="privacy">Privacy</Link>
+ <Link to="/">Home</Link>{' • '}
+ <Link to="/privacy">Privacy</Link>
</p>
</div>
</div>
diff --git a/src/pages/Home.jsx b/src/pages/Home.jsx
@@ -5,40 +5,43 @@
'use strict';
var React = require('react');
+var Layout = require('../layouts/Default.jsx');
var HomePage = React.createClass({
render() {
return (
- <div className="container">
- <div className="row">
- <div className="col-sm-4">
- <h3>Runtime Components</h3>
- <dl>
- <dt><a href="https://facebook.github.io/react/">React</a></dt>
- <dd>A JavaScript library for building user interfaces, developed by Facebook</dd>
- <dt><a href="https://github.com/rackt/react-router">React-Router</a></dt>
- <dd>A complete routing library for React</dd>
- <dt><a href="http://getbootstrap.com/">Bootstrap</a></dt>
- <dd>CSS framework for developing responsive, mobile first interfaces</dd>
- </dl>
- </div>
- <div className="col-sm-4">
- <h3>Development Tools</h3>
- <dl>
- <dt><a href="http://gulpjs.com">Gulp</a></dt>
- <dd>JavaScript streaming build system and task automation</dd>
- <dt><a href="http://webpack.github.io/">Webpack</a></dt>
- <dd>Compiles front-end source code into modules / bundles</dd>
- <dt><a href="http://www.browsersync.io/">BrowserSync</a></dt>
- <dd>A lightweight HTTP server for development</dd>
- </dl>
- </div>
- <div className="col-sm-4">
- <h3>Fork me on GitHub</h3>
- <p><a href="https://github.com/kriasoft/react-starter-kit">github.com/kriasoft/react-starter-kit</a></p>
+ <Layout>
+ <div className="container">
+ <div className="row">
+ <div className="col-sm-4">
+ <h3>Runtime Components</h3>
+ <dl>
+ <dt><a href="https://facebook.github.io/react/">React</a></dt>
+ <dd>A JavaScript library for building user interfaces, developed by Facebook</dd>
+ <dt><a href="https://github.com/rackt/react-router">React-Router</a></dt>
+ <dd>A complete routing library for React</dd>
+ <dt><a href="http://getbootstrap.com/">Bootstrap</a></dt>
+ <dd>CSS framework for developing responsive, mobile first interfaces</dd>
+ </dl>
+ </div>
+ <div className="col-sm-4">
+ <h3>Development Tools</h3>
+ <dl>
+ <dt><a href="http://gulpjs.com">Gulp</a></dt>
+ <dd>JavaScript streaming build system and task automation</dd>
+ <dt><a href="http://webpack.github.io/">Webpack</a></dt>
+ <dd>Compiles front-end source code into modules / bundles</dd>
+ <dt><a href="http://www.browsersync.io/">BrowserSync</a></dt>
+ <dd>A lightweight HTTP server for development</dd>
+ </dl>
+ </div>
+ <div className="col-sm-4">
+ <h3>Fork me on GitHub</h3>
+ <p><a href="https://github.com/kriasoft/react-starter-kit">github.com/kriasoft/react-starter-kit</a></p>
+ </div>
</div>
</div>
- </div>
+ </Layout>
);
}
});
diff --git a/src/pages/Privacy.jsx b/src/pages/Privacy.jsx
@@ -5,53 +5,56 @@
'use strict';
var React = require('react');
+var Layout = require('../layouts/Default.jsx');
var PrivacyPage = React.createClass({
render() {
return (
- <div className="container">
- <h2>Privacy Policy</h2>
- <p>
- Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aenean consequat tortor fermentum mi
- fermentum dignissim. Nullam vel ipsum ut ligula elementum lobortis. Maecenas aliquam, massa laoreet
- lacinia pretium, nisi urna venenatis tortor, nec imperdiet tellus libero efficitur metus. Fusce
- semper posuere ligula, et facilisis metus bibendum interdum. Mauris at mauris sit amet sem pharetra
- commodo a eu leo. Nam at est non risus cursus maximus. Nam feugiat augue libero, id consectetur
- tortor bibendum non. Quisque nec fringilla lorem. Nullam efficitur vulputate mauris, nec maximus leo
- dignissim id.
- </p>
- <p>
- In hac habitasse platea dictumst. Duis sagittis dui ac ex suscipit maximus. Morbi pellentesque
- venenatis felis sed convallis. Nulla varius, nibh vitae placerat tempus, mauris sem elementum ipsum,
- eget sollicitudin nisl est vel purus. Fusce malesuada odio velit, non cursus leo fermentum id. Cras
- pharetra sodales fringilla. Etiam quis est a dolor egestas pellentesque. Maecenas non scelerisque
- purus, congue cursus arcu. Donec vel dapibus mi. Mauris maximus posuere placerat. Sed et libero eu
- nibh tristique mollis a eget lectus. Donec interdum augue sollicitudin vehicula hendrerit. Vivamus
- justo orci, molestie ac sollicitudin ac, lobortis at tellus. Etiam rhoncus ullamcorper risus eu
- tempor. Sed porttitor, neque ac efficitur gravida, arcu lacus pharetra dui, in consequat elit tellus
- auctor nulla. Donec placerat elementum diam, vitae imperdiet lectus luctus at.
- </p>
- <p>
- Nullam eu feugiat mi. Quisque nec tristique nisl, dignissim dictum leo. Nam non quam nisi. Donec
- rutrum turpis ac diam blandit, id pulvinar mauris suscipit. Pellentesque tincidunt libero ultricies
- risus iaculis, sit amet consequat velit blandit. Fusce quis varius nulla. Nullam nisi nisi, suscipit
- ut magna quis, feugiat porta nibh. Sed id enim lectus. Suspendisse elementum justo sapien, sit amet
- consequat orci accumsan et. Aliquam ornare ullamcorper sem sed finibus. Nullam ac lacus pulvinar,
- egestas felis ut, accumsan est.
- </p>
- <p>
- Pellentesque sagittis vehicula sem quis luctus. Proin sodales magna in lorem hendrerit aliquam.
- Integer eu varius orci. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere
- cubilia Curae; Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia
- Curae; Ut at mauris nibh. Suspendisse maximus ac eros at vestibulum.
- </p>
- <p>
- Interdum et malesuada fames ac ante ipsum primis in faucibus. Quisque egestas tortor et dui
- consequat faucibus. Nunc vitae odio ornare, venenatis ligula a, vulputate nisl. Aenean congue varius
- ex, sit amet bibendum odio posuere at. Nulla facilisi. In finibus, nulla vitae tincidunt ornare,
- sapien nulla fermentum mauris, sed consectetur tortor arcu eget arcu. Vestibulum vel quam enim.
- </p>
- </div>
+ <Layout>
+ <div className="container">
+ <h2>Privacy Policy</h2>
+ <p>
+ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aenean consequat tortor fermentum mi
+ fermentum dignissim. Nullam vel ipsum ut ligula elementum lobortis. Maecenas aliquam, massa laoreet
+ lacinia pretium, nisi urna venenatis tortor, nec imperdiet tellus libero efficitur metus. Fusce
+ semper posuere ligula, et facilisis metus bibendum interdum. Mauris at mauris sit amet sem pharetra
+ commodo a eu leo. Nam at est non risus cursus maximus. Nam feugiat augue libero, id consectetur
+ tortor bibendum non. Quisque nec fringilla lorem. Nullam efficitur vulputate mauris, nec maximus leo
+ dignissim id.
+ </p>
+ <p>
+ In hac habitasse platea dictumst. Duis sagittis dui ac ex suscipit maximus. Morbi pellentesque
+ venenatis felis sed convallis. Nulla varius, nibh vitae placerat tempus, mauris sem elementum ipsum,
+ eget sollicitudin nisl est vel purus. Fusce malesuada odio velit, non cursus leo fermentum id. Cras
+ pharetra sodales fringilla. Etiam quis est a dolor egestas pellentesque. Maecenas non scelerisque
+ purus, congue cursus arcu. Donec vel dapibus mi. Mauris maximus posuere placerat. Sed et libero eu
+ nibh tristique mollis a eget lectus. Donec interdum augue sollicitudin vehicula hendrerit. Vivamus
+ justo orci, molestie ac sollicitudin ac, lobortis at tellus. Etiam rhoncus ullamcorper risus eu
+ tempor. Sed porttitor, neque ac efficitur gravida, arcu lacus pharetra dui, in consequat elit tellus
+ auctor nulla. Donec placerat elementum diam, vitae imperdiet lectus luctus at.
+ </p>
+ <p>
+ Nullam eu feugiat mi. Quisque nec tristique nisl, dignissim dictum leo. Nam non quam nisi. Donec
+ rutrum turpis ac diam blandit, id pulvinar mauris suscipit. Pellentesque tincidunt libero ultricies
+ risus iaculis, sit amet consequat velit blandit. Fusce quis varius nulla. Nullam nisi nisi, suscipit
+ ut magna quis, feugiat porta nibh. Sed id enim lectus. Suspendisse elementum justo sapien, sit amet
+ consequat orci accumsan et. Aliquam ornare ullamcorper sem sed finibus. Nullam ac lacus pulvinar,
+ egestas felis ut, accumsan est.
+ </p>
+ <p>
+ Pellentesque sagittis vehicula sem quis luctus. Proin sodales magna in lorem hendrerit aliquam.
+ Integer eu varius orci. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere
+ cubilia Curae; Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia
+ Curae; Ut at mauris nibh. Suspendisse maximus ac eros at vestibulum.
+ </p>
+ <p>
+ Interdum et malesuada fames ac ante ipsum primis in faucibus. Quisque egestas tortor et dui
+ consequat faucibus. Nunc vitae odio ornare, venenatis ligula a, vulputate nisl. Aenean congue varius
+ ex, sit amet bibendum odio posuere at. Nulla facilisi. In finibus, nulla vitae tincidunt ornare,
+ sapien nulla fermentum mauris, sed consectetur tortor arcu eget arcu. Vestibulum vel quam enim.
+ </p>
+ </div>
+ </Layout>
);
}
});