commit 800b8388c0090e889263d53c8c4f3c3b5383508c
parent 35fdd34c62a034741fbe877dddc0d3badec7b200
Author: Konstantin Tarkus <hello@tarkus.me>
Date: Thu, 13 Nov 2014 18:47:26 +0300
Move src/AppDispatcher.js to src/core/Dispatcher.js; create Flux Store base class
Diffstat:
10 files changed, 140 insertions(+), 91 deletions(-)
diff --git a/README.md b/README.md
@@ -28,15 +28,15 @@
├── /src/ # The source code of the application
│ ├── /actions/ # Action creators that allow to trigger a dispatch to stores
│ ├── /assets/ # Static files which are copied to ./build on compile
-│ ├── /constants/ # Enumerations used in action creators and stores
│ ├── /components/ # React components. E.g. Navbar.jsx, Calendar.jsx
+│ ├── /constants/ # Enumerations used in action creators and stores
+│ ├── /core/ # Core components (Flux dispatcher, base classes)
│ ├── /images/ # Graphics (.png, .jpg, .svg etc.)
│ ├── /layouts/ # Shared layouts for top-level components
│ ├── /pages/ # Top-level, URL-bound React components
│ ├── /stores/ # Stores contain the application state and logic
│ ├── /styles/ # CSS style sheets (or LESS, SASS, Stylus)
-│ ├── /app.js # The application's bootstrap file, entry point
-│ └── /AppDispatcher.js # The central hub that manages all data flow (see Flux)
+│ ├── /app.js # The application's main file (entry point)
├── /test/ # Unit, integration and load tests
│ ├── /e2e/ # End-to-end tests
│ └── /unit/ # Unit tests
diff --git a/gulpfile.js b/gulpfile.js
@@ -110,11 +110,11 @@ gulp.task('pages', function() {
src.pages = ['src/pages/**/*.js', 'src/pages/404.html'];
var currentPage = {};
+ var Dispatcher = require('./src/core/Dispatcher');
var ActionTypes = require('./src/constants/ActionTypes');
- var AppDispatcher = require('./src/AppDispatcher');
// Capture document.title and other page metadata changes
- AppDispatcher.register(function(payload) {
+ Dispatcher.register(function(payload) {
if (payload.action.actionType == ActionTypes.SET_CURRENT_PAGE)
{
currentPage = payload.action.page;
diff --git a/src/AppDispatcher.js b/src/AppDispatcher.js
@@ -1,47 +0,0 @@
-/*
- * React.js Starter Kit
- * Copyright (c) 2014 Konstantin Tarkus (@koistya), KriaSoft LLC.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE.txt file in the root directory of this source tree.
- */
-
-'use strict';
-
-var {Dispatcher} = require('flux');
-var PayloadSources = require('./constants/PayloadSources');
-var assign = require('object-assign');
-
-/**
- * A singleton that operates as the central hub for application updates.
- * For more information visit https://facebook.github.io/flux/
- */
-var AppDispatcher = assign(new Dispatcher(), {
-
- /**
- * @param {object} action The details of the action, including the action's
- * type and additional data coming from the server.
- */
- handleServerAction(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(action) {
- var payload = {
- source: PayloadSources.VIEW_ACTION,
- action: action
- };
- this.dispatch(payload);
- }
-
-});
-
-module.exports = AppDispatcher;
diff --git a/src/actions/PageActions.js b/src/actions/PageActions.js
@@ -8,7 +8,7 @@
'use strict';
-var AppDispatcher = require('../AppDispatcher');
+var Dispatcher = require('../core/Dispatcher');
var ActionTypes = require('../constants/ActionTypes');
var pageDefaults = require('../constants/Settings').defaults.page;
var assign = require('object-assign');
@@ -20,7 +20,7 @@ module.exports = {
* @param {object} The page object.
*/
set(page) {
- AppDispatcher.handleViewAction({
+ Dispatcher.handleViewAction({
actionType: ActionTypes.SET_CURRENT_PAGE,
page: assign({}, pageDefaults, page)
});
diff --git a/src/actions/RouteActions.js b/src/actions/RouteActions.js
@@ -8,7 +8,7 @@
'use strict';
-var AppDispatcher = require('../AppDispatcher');
+var Dispatcher = require('../core/Dispatcher');
var ActionTypes = require('../constants/ActionTypes');
module.exports = {
@@ -18,7 +18,7 @@ module.exports = {
* @param {string} route Supply a route value, such as `todos/completed`.
*/
setRoute(route) {
- AppDispatcher.handleViewAction({
+ Dispatcher.handleViewAction({
actionType: ActionTypes.SET_CURRENT_ROUTE,
route: route
});
diff --git a/src/app.js b/src/app.js
@@ -11,14 +11,14 @@
var React = require('react');
var ExecutionEnvironment = require('react/lib/ExecutionEnvironment');
var {Router} = require('director');
-var AppDispatcher = require('./AppDispatcher');
+var Dispatcher = require('./core/Dispatcher');
var ActionTypes = require('./constants/ActionTypes');
var router;
// Export React so the dev tools can find it
(window !== window.top ? window.top : window).React = React;
-AppDispatcher.register((payload) => {
+Dispatcher.register((payload) => {
var action = payload.action;
@@ -28,9 +28,9 @@ AppDispatcher.register((payload) => {
router.setRoute(action.route);
break;
- case ActionTypes.SET_PAGE_TITLE:
+ case ActionTypes.SET_CURRENT_PAGE:
if (ExecutionEnvironment.canUseDOM) {
- document.title = action.text;
+ document.title = action.page.title;
}
break;
}
diff --git a/src/core/Dispatcher.js b/src/core/Dispatcher.js
@@ -0,0 +1,47 @@
+/*
+ * React.js Starter Kit
+ * Copyright (c) 2014 Konstantin Tarkus (@koistya), KriaSoft LLC.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE.txt file in the root directory of this source tree.
+ */
+
+'use strict';
+
+var Flux = require('flux');
+var PayloadSources = require('../constants/PayloadSources');
+var assign = require('object-assign');
+
+/**
+ * A singleton that operates as the central hub for application updates.
+ * For more information visit https://facebook.github.io/flux/
+ */
+var Dispatcher = assign(new Flux.Dispatcher(), {
+
+ /**
+ * @param {object} action The details of the action, including the action's
+ * type and additional data coming from the server.
+ */
+ handleServerAction(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(action) {
+ var payload = {
+ source: PayloadSources.VIEW_ACTION,
+ action: action
+ };
+ this.dispatch(payload);
+ }
+
+});
+
+module.exports = Dispatcher;
diff --git a/src/core/Store.js b/src/core/Store.js
@@ -0,0 +1,69 @@
+/*
+ * React.js Starter Kit
+ * Copyright (c) 2014 Konstantin Tarkus (@koistya), KriaSoft LLC.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE.txt file in the root directory of this source tree.
+ */
+
+'use strict';
+
+var EventEmitter = require('events').EventEmitter;
+var assign = require('object-assign');
+var invariant = require('react/lib/invariant');
+
+var CHANGE_EVENT = 'change';
+
+class Store {
+
+ constructor(methods) {
+
+ var self = this;
+
+ invariant(!methods.dispatcherToken,'"dispatcherToken" is a reserved name and cannot be used as a method name.');
+ invariant(!methods.mixin,'"mixin" is a reserved name and cannot be used as a method name.');
+
+ assign(this, EventEmitter.prototype, methods);
+
+ this.dispatcherToken = null;
+ this.mixin = {
+
+ componentDidMount: function() {
+ self.addChangeListener(this.onChange);
+ },
+
+ componentWillUnmount: function() {
+ self.removeChangeListener(this.onChange);
+ }
+
+ };
+ }
+
+ /**
+ * Emits change event.
+ */
+ emitChange() {
+ this.emit(CHANGE_EVENT);
+ }
+
+ /**
+ * Adds a change listener.
+ *
+ * @param {function} callback Callback function.
+ */
+ addChangeListener(callback) {
+ this.on(CHANGE_EVENT, callback);
+ }
+
+ /**
+ * Removes a change listener.
+ *
+ * @param {function} callback Callback function.
+ */
+ removeChangeListener(callback) {
+ this.removeListener(CHANGE_EVENT, callback);
+ }
+
+}
+
+module.exports = Store;
diff --git a/src/layouts/DefaultLayout.js b/src/layouts/DefaultLayout.js
@@ -14,7 +14,7 @@ var Link = require('../components/Link');
var Navbar = require('../components/Navbar');
/**
- * Retrieve the current page metadata from the PageStore.
+ * Retrieves the current page metadata from the PageStore.
* @returns {{title: string}}
*/
function getState() {
@@ -25,19 +25,16 @@ function getState() {
var DefaultLayout = React.createClass({
+ mixins: [PageStore.mixin],
+
getInitialState() {
return getState();
},
componentDidMount() {
- PageStore.addEventListener(this._onChange);
PageStore.emitChange();
},
- componentWillUnmount() {
- PageStore.removeEventListener(this._onChange);
- },
-
render() {
var header = this.props.children.type.breadcrumb ? (
<div className="container">
@@ -72,9 +69,9 @@ var DefaultLayout = React.createClass({
},
/**
- * Event handler for 'change' events coming from the PageStore
+ * Event handler for 'change' events coming from the PageStore.
*/
- _onChange() {
+ onChange() {
this.setState(getState());
}
});
diff --git a/src/stores/PageStore.js b/src/stores/PageStore.js
@@ -8,12 +8,9 @@
'use strict';
-var AppDispatcher = require('../AppDispatcher');
+var Store = require('../core/Store');
+var Dispatcher = require('../core/Dispatcher');
var ActionTypes = require('../constants/ActionTypes');
-var EventEmitter = require('events').EventEmitter;
-var assign = require('object-assign');
-
-var CHANGE_EVENT = 'change';
/**
* @typedef Page
@@ -22,36 +19,21 @@ var CHANGE_EVENT = 'change';
* @property {string} description
* @property {string} keywords
*/
-
-/** @type {Page} */
var _page;
-var PageStore = assign({}, EventEmitter.prototype, {
+var PageStore = new Store({
/**
- * Get the current page.
+ * Gets metadata associated with the current page.
* @returns {Page}
*/
get() {
return _page || require('../constants/Settings').defaults.page;
- },
-
- emitChange() {
- this.emit(CHANGE_EVENT);
- },
-
- addEventListener(callback) {
- this.on(CHANGE_EVENT, callback);
- },
-
- removeEventListener(callback) {
- this.removeListener(CHANGE_EVENT, callback);
}
});
-
-AppDispatcher.register(function(payload) {
+PageStore.dispatcherToken = Dispatcher.register(payload => {
var action = payload.action;
@@ -61,6 +43,7 @@ AppDispatcher.register(function(payload) {
}
return true; // No errors. Needed by promise in Dispatcher.
+
});
module.exports = PageStore;