10 | );
11 | }
12 | }
13 |
14 | export default About;
15 |
--------------------------------------------------------------------------------
/packages/fluxible-addons-react/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | Contributing Code to `fluxible`
2 | -------------------------------
3 |
4 | Please be sure to sign our [CLA][] before you submit pull requests or otherwise contribute to `fluxible`. This protects developers, who rely on [BSD license][].
5 |
6 | [BSD license]: https://github.com/yahoo/fluxible/blob/master/LICENSE.md
7 | [CLA]: https://yahoocla.herokuapp.com/
8 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Fluxible
2 |
3 | This repository is the home of Fluxible and related libraries.
4 |
5 | Join the #fluxible channel of the [Reactiflux](http://reactiflux.com) Discord community.
6 |
7 | ## License
8 |
9 | This software is free to use under the Yahoo Inc. BSD license.
10 | See the [LICENSE file][] for license text and copyright information.
11 |
12 | [LICENSE file]: https://github.com/yahoo/fluxible/blob/master/LICENSE.md
13 |
--------------------------------------------------------------------------------
/packages/generator-fluxible/app/templates/configs/routes.js:
--------------------------------------------------------------------------------
1 | export default {
2 | home: {
3 | path: '/',
4 | method: 'get',
5 | page: 'home',
6 | title: 'Home',
7 | handler: require('../components/Home')
8 | },
9 | about: {
10 | path: '/about',
11 | method: 'get',
12 | page: 'about',
13 | title: 'About',
14 | handler: require('../components/About')
15 | }
16 | };
17 |
--------------------------------------------------------------------------------
/packages/fluxible/tests/fixtures/stores/BarStore.js:
--------------------------------------------------------------------------------
1 | var createStore = require('../../../addons/createStore');
2 | module.exports = createStore({
3 | storeName: 'BarStore',
4 | handlers: {
5 | 'DOUBLE_UP': function () {
6 | this.bar += this.bar;
7 | this.emitChange();
8 | }
9 | },
10 | initialize: function () {
11 | this.bar = 'baz';
12 | },
13 | getBar: function () {
14 | return this.bar;
15 | }
16 | });
17 |
--------------------------------------------------------------------------------
/packages/fluxible/tests/fixtures/stores/FooStore.js:
--------------------------------------------------------------------------------
1 | var createStore = require('../../../addons/createStore');
2 |
3 | module.exports = createStore({
4 | storeName: 'FooStore',
5 | handlers: {
6 | 'DOUBLE_UP': function () {
7 | this.foo += this.foo;
8 | this.emitChange();
9 | }
10 | },
11 | initialize: function () {
12 | this.foo = 'bar';
13 | },
14 | getFoo: function () {
15 | return this.foo;
16 | }
17 | });
18 |
--------------------------------------------------------------------------------
/packages/fluxible-addons-react/tests/fixtures/stores/BarStore.js:
--------------------------------------------------------------------------------
1 | var createStore = require('fluxible/addons/createStore');
2 | module.exports = createStore({
3 | storeName: 'BarStore',
4 | handlers: {
5 | 'DOUBLE_UP': function () {
6 | this.bar += this.bar;
7 | this.emitChange();
8 | }
9 | },
10 | initialize: function () {
11 | this.bar = 'baz';
12 | },
13 | getBar: function () {
14 | return this.bar;
15 | }
16 | });
17 |
--------------------------------------------------------------------------------
/packages/generator-fluxible/app/templates/app.js:
--------------------------------------------------------------------------------
1 | import Fluxible from 'fluxible';
2 | import Application from './components/Application';
3 | import ApplicationStore from './stores/ApplicationStore';
4 | import RouteStore from './stores/RouteStore';
5 |
6 | // create new fluxible instance
7 | const app = new Fluxible({
8 | component: Application
9 | });
10 |
11 | // register stores
12 | app.registerStore(RouteStore);
13 | app.registerStore(ApplicationStore);
14 |
15 | module.exports = app;
16 |
--------------------------------------------------------------------------------
/packages/fluxible-addons-react/tests/fixtures/stores/FooStore.js:
--------------------------------------------------------------------------------
1 | var createStore = require('fluxible/addons/createStore');
2 |
3 | module.exports = createStore({
4 | storeName: 'FooStore',
5 | handlers: {
6 | 'DOUBLE_UP': function () {
7 | this.foo += this.foo;
8 | this.emitChange();
9 | }
10 | },
11 | initialize: function () {
12 | this.foo = 'bar';
13 | },
14 | getFoo: function () {
15 | return this.foo;
16 | }
17 | });
18 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | sudo: false
2 | language: node_js
3 | matrix:
4 | allow_failures:
5 | - node_js: "iojs"
6 | node_js:
7 | - "iojs"
8 | - "0.12"
9 | - "0.10"
10 | env:
11 | - TEST_DIR=packages/fluxible
12 | - TEST_DIR=packages/fluxible-reducer-store
13 | - TEST_DIR=packages/fluxible-addons-react
14 | - TEST_DIR=packages/generator-fluxible
15 | script: cd $TEST_DIR && npm test
16 | after_success:
17 | - "npm run cover"
18 | - "cat artifacts/lcov.info | ./node_modules/coveralls/bin/coveralls.js"
19 |
--------------------------------------------------------------------------------
/packages/fluxible-addons-react/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2015, Yahoo Inc.
3 | * Copyrights licensed under the New BSD License. See the accompanying LICENSE file for terms.
4 | */
5 | 'use strict';
6 |
7 | module.exports = {
8 | batchedUpdatePlugin: require('./batchedUpdatePlugin'),
9 | connectToStores: require('./connectToStores'),
10 | createElementWithContext: require('./createElementWithContext'),
11 | FluxibleComponent: require('./FluxibleComponent'),
12 | FluxibleMixin: require('./FluxibleMixin'),
13 | provideContext: require('./provideContext')
14 | };
15 |
--------------------------------------------------------------------------------
/packages/fluxible-reducer-store/tests/fixtures/stores/SingleReducerStore.js:
--------------------------------------------------------------------------------
1 | import {createReducerStore} from '../../../index';
2 |
3 | var SingleReducerStore = createReducerStore({
4 | storeName: 'SingleReducerStore',
5 | reducer: (state, payload, type) => {
6 | if ('RECEIVE_MESSAGES' === type) {
7 | return {
8 | ...state,
9 | count: state.count + (payload.length)
10 | };
11 | }
12 | return state;
13 | },
14 | initialState: {
15 | count: 0
16 | }
17 | });
18 |
19 | export default SingleReducerStore;
20 |
--------------------------------------------------------------------------------
/packages/generator-fluxible/app/templates/webpack-dev-server.js:
--------------------------------------------------------------------------------
1 | var webpack = require('webpack');
2 | var WebpackDevServer = require('webpack-dev-server');
3 | var config = require('./webpack.config');
4 |
5 | new WebpackDevServer(webpack(config), {
6 | publicPath: config.output.publicPath,
7 | hot: true,
8 | historyApiFallback: true,
9 | //quiet: true,
10 | proxy: {
11 | '*': { target: 'http://localhost:3001' }
12 | }
13 | }).listen(3000, function () {
14 | shell.env.PORT = shell.env.PORT || 3001;
15 | shell.exec('"./node_modules/.bin/nodemon" start.js -e js,jsx', function () {});
16 | console.log('Webpack Dev Server listening on port 3000');
17 | });
18 |
--------------------------------------------------------------------------------
/packages/fluxible-addons-react/docs/api/batchedUpdatePlugin.md:
--------------------------------------------------------------------------------
1 | # batchedUpdatePlugin
2 |
3 | ```js
4 | import batchedUpdatePlugin from 'fluxible-addons-react/batchedUpdatePlugin';
5 | ```
6 |
7 | `batchedUpdatePlugin` is a Fluxible plugin that will batch React `setState`
8 | calls together when they are part of the same `dispatch`. This can improve
9 | performance as there will be less re-renders for components that listen
10 | to multiple stores that react to the same dispatch command.
11 |
12 | ## Example
13 |
14 | The plugin is added to Fluxible immediately after instantiation as follows:
15 |
16 | ```js
17 | var app = new Fluxible();
18 | app.plug(batchedUpdatePlugin());
19 | ```
20 |
21 | This will wrap the `actionContext.dispatch` function with a call to
22 | `React.addons.batchedUpdate`.
23 |
--------------------------------------------------------------------------------
/docs/quick-start.md:
--------------------------------------------------------------------------------
1 | # Quick Start
2 |
3 | ```bash
4 | npm install -g yo generator-fluxible
5 | ```
6 |
7 | To use the generator, create a directory and cd into it. Then run `yo fluxible` which will create a working Fluxible application. To start the application, run `npm run dev`. View it in a browser at http://localhost:3000.
8 |
9 | ```bash
10 | mkdir example && cd example
11 | yo fluxible
12 | npm run dev
13 | ```
14 |
15 | `open http://localhost:3000`
16 |
17 | This will generate a simple application that demonstrates the basics of using Fluxible: routing, store dehydration from server, and client rehydrating.
18 |
19 | From here, we recommend learning about [stores](../packages/fluxible/docs/api/Stores.md), [actions](../packages/fluxible/docs/api/Actions.md), and React integration with your [components](../packages/fluxible/docs/api/Components.md).
20 |
--------------------------------------------------------------------------------
/packages/fluxible/utils/createMockActionContext.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2014, Yahoo! Inc.
3 | * Copyrights licensed under the New BSD License. See the accompanying LICENSE file for terms.
4 | */
5 | 'use strict';
6 |
7 | var dispatchr = require('dispatchr');
8 | var MockActionContextClass = require('./MockActionContext');
9 |
10 | module.exports = function createMockActionContext(options) {
11 | options = options || {};
12 | options.mockActionContextClass = options.mockActionContextClass || MockActionContextClass;
13 | options.stores = options.stores || [];
14 | options.dispatcher = options.dispatcher || dispatchr.createDispatcher({
15 | stores: options.stores
16 | });
17 | options.dispatcherContext = options.dispatcherContext || options.dispatcher.createContext();
18 |
19 | return new options.mockActionContextClass(options.dispatcherContext);
20 | };
21 |
--------------------------------------------------------------------------------
/packages/fluxible/utils/createMockComponentContext.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2014, Yahoo! Inc.
3 | * Copyrights licensed under the New BSD License. See the accompanying LICENSE file for terms.
4 | */
5 | 'use strict';
6 |
7 | var dispatchr = require('dispatchr');
8 | var MockComponentContextClass = require('./MockComponentContext');
9 |
10 | module.exports = function createMockComponentContext(options) {
11 | options = options || {};
12 | options.mockComponentContextClass = options.mockComponentContextClass || MockComponentContextClass;
13 | options.stores = options.stores || [];
14 | options.dispatcher = options.dispatcher || dispatchr.createDispatcher({
15 | stores: options.stores
16 | });
17 | options.dispatcherContext = options.dispatcherContext || options.dispatcher.createContext();
18 |
19 | return new options.mockComponentContextClass(options.dispatcherContext);
20 | };
21 |
--------------------------------------------------------------------------------
/packages/fluxible-addons-react/docs/api/createElementWithContext.md:
--------------------------------------------------------------------------------
1 | # createElementWithContext(context, props)
2 |
3 | ```js
4 | import createElementWithContext from 'fluxible-addons-react/createElementWithContext';
5 | ```
6 |
7 | Convenience method for instantiating the Fluxible app's top level React
8 | component (if provided in the constructor) with the given props with an
9 | additional `context` key containing a ComponentContext.
10 |
11 | ```js
12 | const app = new Fluxible({
13 | component: MyComponent
14 | });
15 | const context = app.createContext();
16 | const markup = ReactDOM.renderToString(createElementWithContext(context, props));
17 | ```
18 |
19 | This is the same as the following:
20 |
21 | ```js
22 | const markup = ReactDOM.renderToString(
23 |
24 |
25 |
26 | );
27 | ```
28 |
--------------------------------------------------------------------------------
/packages/fluxible/docs/api/addons/createStore.md:
--------------------------------------------------------------------------------
1 | # createStore
2 |
3 | ```js
4 | import createStore from 'fluxible-addons-react/createStore';
5 | ```
6 |
7 | A helper method similar to `React.createClass` but for creating stores that extend [`BaseStore`](BaseStore.md). Also supports mixins.
8 |
9 | ## Example
10 |
11 | ```js
12 | export default createStore({
13 | storeName: 'ApplicationStore',
14 | handlers: {
15 | 'RECEIVE_PAGE': 'handleReceivePage'
16 | },
17 | handleReceivePage: function (payload) {
18 | this.currentPageName = payload.pageName;
19 | this.emitChange();
20 | },
21 | getCurrentPage: function () {
22 | return this.currentPageName;
23 | },
24 | dehydrate: function () {
25 | return {
26 | currentPageName: this.currentPageName
27 | };
28 | },
29 | rehydrate: function (state) {
30 | this.currentPageName = state.currentPageName;
31 | }
32 | });
33 | ```
34 |
--------------------------------------------------------------------------------
/docs/README.md:
--------------------------------------------------------------------------------
1 | # Docs
2 |
3 | If you want to jump right into building your application, head over to our [Quick Start Guide](quick-start.md).
4 |
5 | If you want to look at example applications, check out our examples:
6 |
7 | - [Fluxible.io](https://github.com/yahoo/fluxible.io) - Our very own documentation site written with Fluxible.
8 | - [Chat](https://github.com/yahoo/flux-examples/tree/master/chat) - Port of [Facebook's Flux chat example](https://github.com/facebook/flux/tree/master/examples/).
9 | - [Fluxible Router](https://github.com/yahoo/flux-examples/tree/master/fluxible-router) - Simple isomorphic routing in Flux flow.
10 | - [React Router](https://github.com/yahoo/flux-examples/tree/master/react-router) - Simple isomorphic routing with React router.
11 | - [TodoMVC](https://github.com/yahoo/flux-examples/tree/master/todo) - [TodoMVC](https://github.com/tastejs/todomvc) example using Fluxible.
12 |
13 | Or check out some [community applications](community/reference-applications.md).
14 |
--------------------------------------------------------------------------------
/packages/fluxible/utils/deprecateComponent.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2015, Yahoo Inc.
3 | * Copyrights licensed under the New BSD License. See the accompanying LICENSE file for terms.
4 | */
5 | 'use strict';
6 |
7 | var React = require('react');
8 |
9 | /**
10 | * Deprecates a component by logging a warning message when used
11 | * @method deprecateComponent
12 | * @param {React.Component} Component component to wrap
13 | * @param {string} warningMessage Custom contextTypes to add
14 | * @returns {React.Component}
15 | */
16 | module.exports = function deprecateComponent(Component, warningMessage) {
17 | var DeprecationComponent = React.createClass({
18 | displayName: 'DeprecationComponent',
19 |
20 | componentWillMount: function () {
21 | console.warn(warningMessage);
22 | },
23 |
24 | render: function () {
25 | return React.createElement(Component, this.props);
26 | }
27 | });
28 |
29 | return DeprecationComponent;
30 | };
31 |
--------------------------------------------------------------------------------
/packages/generator-fluxible/app/templates/components/Html.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ApplicationStore from '../stores/ApplicationStore';
3 |
4 | class Html extends React.Component {
5 | render() {
6 | return (
7 |
8 |
9 |
10 | {this.props.context.getStore(ApplicationStore).getPageTitle()}
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 | );
21 | }
22 | }
23 |
24 | export default Html;
25 |
--------------------------------------------------------------------------------
/packages/fluxible/utils/MockComponentContext.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2014, Yahoo! Inc.
3 | * Copyrights licensed under the New BSD License. See the accompanying LICENSE file for terms.
4 | */
5 | 'use strict';
6 |
7 | var createMockActionContext = require('./createMockActionContext');
8 | function noop () {}
9 |
10 | function MockComponentContext (dispatcherContext) {
11 | this.dispatcherContext = dispatcherContext;
12 | this.executeActionCalls = [];
13 | this.getStore = this.getStore.bind(this);
14 | this.executeAction = this.executeAction.bind(this);
15 | }
16 |
17 | MockComponentContext.prototype.getStore = function (name) {
18 | return this.dispatcherContext.getStore(name);
19 | };
20 |
21 | MockComponentContext.prototype.executeAction = function (action, payload) {
22 | this.executeActionCalls.push({
23 | action: action,
24 | payload: payload
25 | });
26 | action(createMockActionContext({
27 | dispatcherContext: this.dispatcherContext
28 | }), payload, noop);
29 | };
30 |
31 | module.exports = MockComponentContext;
32 |
--------------------------------------------------------------------------------
/packages/generator-fluxible/app/templates/stores/ApplicationStore.js:
--------------------------------------------------------------------------------
1 | import BaseStore from 'fluxible/addons/BaseStore';
2 | import RouteStore from './RouteStore';
3 |
4 | class ApplicationStore extends BaseStore {
5 | constructor(dispatcher) {
6 | super(dispatcher);
7 | this.pageTitle = '';
8 | }
9 | handlePageTitle(currentRoute) {
10 | this.dispatcher.waitFor(RouteStore, () => {
11 | if (currentRoute && currentRoute.get('title')) {
12 | this.pageTitle = currentRoute.get('title');
13 | this.emitChange();
14 | }
15 | });
16 | }
17 | getPageTitle() {
18 | return this.pageTitle;
19 | }
20 | dehydrate() {
21 | return {
22 | pageTitle: this.pageTitle
23 | };
24 | }
25 | rehydrate(state) {
26 | this.pageTitle = state.pageTitle;
27 | }
28 | }
29 |
30 | ApplicationStore.storeName = 'ApplicationStore';
31 | ApplicationStore.handlers = {
32 | 'NAVIGATE_SUCCESS': 'handlePageTitle'
33 | };
34 |
35 | export default ApplicationStore;
36 |
--------------------------------------------------------------------------------
/packages/fluxible-addons-react/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "fluxible-addons-react",
3 | "version": "0.2.0",
4 | "description": "Fluxible addons for use with React",
5 | "main": "index.js",
6 | "repository": {
7 | "type": "git",
8 | "url": "https://github.com/yahoo/fluxible"
9 | },
10 | "scripts": {
11 | "test": "../../node_modules/.bin/mocha tests/unit/ --recursive --compilers js:babel/register --reporter spec",
12 | "cover": "../../node_modules/.bin/istanbul cover --dir artifacts -- ../../node_modules/.bin/_mocha tests/unit/ --recursive --compilers js:babel/register --reporter spec",
13 | "lint": "../../node_modules/.bin/eslint *.js tests/"
14 | },
15 | "dependencies": {
16 | "hoist-non-react-statics": "^1.0.0"
17 | },
18 | "peerDependencies": {
19 | "fluxible": ">=1.0.0",
20 | "react": "^0.14.0",
21 | "react-dom": "^0.14.0"
22 | },
23 | "devDependencies": {
24 | "fluxible": ">=1.0.0",
25 | "react": "^0.14.0",
26 | "react-dom": "^0.14.0"
27 | },
28 | "author": "Michael Ridgway ",
29 | "license": "BSD-3-Clause"
30 | }
31 |
--------------------------------------------------------------------------------
/packages/fluxible/utils/MockActionContext.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2014, Yahoo! Inc.
3 | * Copyrights licensed under the New BSD License. See the accompanying LICENSE file for terms.
4 | */
5 | 'use strict';
6 | var callAction = require('./callAction');
7 |
8 | function MockActionContext (dispatcherContext) {
9 | this.dispatcherContext = dispatcherContext;
10 | this.executeActionCalls = [];
11 | this.dispatchCalls = [];
12 | }
13 |
14 | MockActionContext.prototype.getStore = function (name) {
15 | return this.dispatcherContext.getStore(name);
16 | };
17 |
18 | MockActionContext.prototype.dispatch = function (name, payload) {
19 | this.dispatchCalls.push({
20 | name: name,
21 | payload: payload
22 | });
23 | this.dispatcherContext.dispatch(name, payload);
24 | };
25 |
26 | MockActionContext.prototype.executeAction = function (action, payload, callback) {
27 | this.executeActionCalls.push({
28 | action: action,
29 | payload: payload
30 | });
31 | return callAction(action, this, payload, callback);
32 | };
33 |
34 | module.exports = MockActionContext;
35 |
--------------------------------------------------------------------------------
/packages/generator-fluxible/tests/unit/test-app.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2015, Yahoo! Inc.
3 | * Copyrights licensed under the New BSD License. See the accompanying LICENSE file for terms.
4 | */
5 | /*globals describe,before,it*/
6 |
7 | var path = require('path');
8 | var assert = require('yeoman-generator').assert;
9 | var helpers = require('yeoman-generator').test;
10 | var os = require('os');
11 |
12 | describe('generator-fluxible', function () {
13 | describe('app', function () {
14 | before(function (done) {
15 | helpers.run(path.join(__dirname, '../../app'))
16 | .inDir(path.join(os.tmpdir(), './temp-test'))
17 | .withOptions({ 'skip-install': true })
18 | .on('end', done);
19 | });
20 |
21 | it('creates files', function () {
22 | assert.file([
23 | 'package.json',
24 | '.editorconfig',
25 | '.babelrc',
26 | '.eslintrc',
27 | 'app.js',
28 | 'components/Application.js'
29 | ]);
30 | });
31 | });
32 | });
33 |
--------------------------------------------------------------------------------
/packages/fluxible-reducer-store/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "fluxible-reducer-store",
3 | "version": "0.1.0",
4 | "description": "Reducer store for Fluxible",
5 | "main": "index.js",
6 | "repository": {
7 | "type": "git",
8 | "url": "https://github.com/yahoo/fluxible"
9 | },
10 | "scripts": {
11 | "test": "../../node_modules/.bin/mocha tests/unit/ --recursive --compilers js:babel/register --reporter spec",
12 | "cover": "../../node_modules/.bin/istanbul cover --dir artifacts -- ../../node_modules/.bin/_mocha tests/unit/ --recursive --compilers js:babel/register --reporter spec",
13 | "lint": "../../node_modules/.bin/eslint *.js"
14 | },
15 | "dependencies": {
16 | "fluxible": ">0.3.0"
17 | },
18 | "author": "Michael Ridgway ",
19 | "contributors": [],
20 | "licenses": [
21 | {
22 | "type": "BSD-3-Clause",
23 | "url": "https://github.com/yahoo/fluxible/blob/master/LICENSE.md"
24 | }
25 | ],
26 | "keywords": [
27 | "yahoo",
28 | "flux",
29 | "react",
30 | "fluxible",
31 | "isomorphic",
32 | "reducer"
33 | ]
34 | }
35 |
--------------------------------------------------------------------------------
/packages/fluxible-addons-react/FluxibleComponent.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2015, Yahoo Inc.
3 | * Copyrights licensed under the New BSD License. See the accompanying LICENSE file for terms.
4 | */
5 | 'use strict';
6 |
7 | var React = require('react');
8 |
9 | var FluxibleComponent = React.createClass({
10 | displayName: 'FluxibleComponent',
11 | propTypes: {
12 | context: React.PropTypes.object.isRequired
13 | },
14 |
15 | childContextTypes: {
16 | executeAction: React.PropTypes.func.isRequired,
17 | getStore: React.PropTypes.func.isRequired
18 | },
19 |
20 | /**
21 | * Provides the current context as a child context
22 | * @method getChildContext
23 | */
24 | getChildContext: function () {
25 | return {
26 | getStore: this.props.context.getStore,
27 | executeAction: this.props.context.executeAction
28 | };
29 | },
30 |
31 | render: function () {
32 | return React.cloneElement(this.props.children, {
33 | context: this.props.context
34 | });
35 | }
36 | });
37 |
38 | module.exports = FluxibleComponent;
39 |
--------------------------------------------------------------------------------
/packages/generator-fluxible/app/templates/components/Nav.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { NavLink } from 'fluxible-router';
3 |
4 | class Nav extends React.Component {
5 | render() {
6 | const selected = this.props.currentRoute;
7 | const links = this.props.links;
8 |
9 | const linkHTML = Object.keys(links).map((name) => {
10 | var className = '';
11 | var link = links[name];
12 |
13 | if (selected && selected.get('name') === name) {
14 | className = 'pure-menu-selected';
15 | }
16 |
17 | return (
18 |
19 | {link.title}
20 |
21 | );
22 | });
23 |
24 | return (
25 |
26 | {linkHTML}
27 |
28 | );
29 | }
30 | }
31 |
32 | Nav.defaultProps = {
33 | selected: null,
34 | links: {}
35 | };
36 |
37 | export default Nav;
38 |
--------------------------------------------------------------------------------
/packages/generator-fluxible/app/templates/client.js:
--------------------------------------------------------------------------------
1 | /*global document, window */
2 |
3 | import ReactDOM from 'react-dom';
4 | import React from 'react';
5 | import debug from 'debug';
6 | import { createElementWithContext } from 'fluxible-addons-react';
7 | import app from './app';
8 |
9 | const debugClient = debug('<%= name %>');
10 | const dehydratedState = window.App; // Sent from the server
11 |
12 | window.React = ReactDOM; // For chrome dev tool support
13 |
14 | // expose debug object to browser, so that it can be enabled/disabled from browser:
15 | // https://github.com/visionmedia/debug#browser-support
16 | window.fluxibleDebug = debug;
17 |
18 | debugClient('rehydrating app');
19 |
20 | // pass in the dehydrated server state from server.js
21 | app.rehydrate(dehydratedState, (err, context) => {
22 | if (err) {
23 | throw err;
24 | }
25 | window.context = context;
26 | const mountNode = document.getElementById('app');
27 |
28 | debugClient('React Rendering');
29 | ReactDOM.render(
30 | createElementWithContext(context),
31 | mountNode,
32 | () => debugClient('React Rendered')
33 | );
34 | });
35 |
--------------------------------------------------------------------------------
/docs/home.md:
--------------------------------------------------------------------------------
1 | # Features
2 |
3 | ## Singleton-free for server rendering
4 |
5 | [Stores](../packages/fluxible/docs/api/Stores.md) are classes that are instantiated per request or client session. This ensures that the stores are isolated and do not bleed information between requests.
6 |
7 | ## Dehydration/Rehydration
8 |
9 | [Stores](../packages/fluxible/docs/api/Stores.md) can provide `dehydrate` and `rehydrate` so that you can propagate the initial server state to the client.
10 |
11 | ## React Integration
12 |
13 | Helper utilities for integrating your Fluxible app into React [components](../packages/fluxible/docs/api/Components.md) with less boilerplate.
14 |
15 | ## Flow Regulation
16 |
17 | [FluxibleContext](../packages/fluxible/docs/api/FluxibleContext.md) restricts access to your Flux methods so that you can't break out of the unidirectional flow.
18 |
19 | ## Pluggable
20 |
21 | Want to add your own interfaces to the Flux flow? [Plugins](../packages/fluxible/docs/api/Plugins.md) allow you to add methods to any of the contexts.
22 |
23 | ## Updated for React 0.13
24 |
25 | Updated to follow React 0.13's changes and the deprecations coming in the next version of React.
26 |
--------------------------------------------------------------------------------
/packages/fluxible/utils/callAction.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2014, Yahoo! Inc.
3 | * Copyrights licensed under the New BSD License. See the accompanying LICENSE file for terms.
4 | */
5 | /* global Promise */
6 | 'use strict';
7 | var isPromise = require('is-promise');
8 |
9 | /**
10 | * Call an action supporting Promise expectations on invocation.
11 | *
12 | * If done callback supplied, that indicates non-Promise invocation expectation,
13 | * otherwise, Promise invocation.
14 | */
15 | function callAction (action, context, payload, done) {
16 | if (done) {
17 | return action(context, payload, done);
18 | }
19 |
20 | return new Promise(function (resolve, reject) {
21 | try {
22 | var syncResult = action(context, payload, function (err, result) {
23 | if (err) {
24 | reject(err);
25 | } else {
26 | resolve(result);
27 | }
28 | });
29 | if (isPromise(syncResult)) {
30 | syncResult.then(resolve, reject);
31 | } else if (action.length < 3) {
32 | resolve(syncResult);
33 | }
34 | } catch (e) {
35 | reject(e);
36 | }
37 | });
38 | }
39 |
40 | module.exports = callAction;
41 |
--------------------------------------------------------------------------------
/packages/fluxible/tests/fixtures/plugins/DimensionsContextPluginSync.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2014, Yahoo! Inc.
3 | * Copyrights licensed under the New BSD License. See the accompanying LICENSE file for terms.
4 | */
5 | 'use strict';
6 |
7 | module.exports = function (dims) {
8 | var dimensions = dims;
9 | return {
10 | name: 'DimensionsPlugin',
11 | plugActionContext: function (actionContext) {
12 | actionContext.getDimensions = function () {
13 | return dimensions;
14 | };
15 | },
16 | plugComponentContext: function (componentContext) {
17 | componentContext.getDimensions = function () {
18 | return dimensions;
19 | };
20 | },
21 | plugStoreContext: function (storeContext) {
22 | storeContext.getDimensions = function () {
23 | return dimensions;
24 | };
25 | },
26 | dehydrate: function () {
27 | return {
28 | dimensions: dimensions
29 | };
30 | },
31 | rehydrate: function (state) {
32 | dimensions = state.dimensions;
33 | }
34 | }
35 | };
36 |
--------------------------------------------------------------------------------
/packages/fluxible/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "fluxible",
3 | "version": "1.0.0",
4 | "description": "A pluggable container for isomorphic flux applications",
5 | "main": "index.js",
6 | "repository": {
7 | "type": "git",
8 | "url": "https://github.com/yahoo/fluxible"
9 | },
10 | "scripts": {
11 | "test": "../../node_modules/.bin/mocha tests/unit/ --recursive --compilers js:babel/register --reporter spec",
12 | "cover": "../../node_modules/.bin/istanbul cover --dir artifacts -- ../../node_modules/.bin/_mocha tests/unit/ --recursive --compilers js:babel/register --reporter spec",
13 | "lint": "../../node_modules/.bin/eslint lib/ addons/"
14 | },
15 | "dependencies": {
16 | "debug": "^2.0.0",
17 | "dispatchr": "^0.3.1",
18 | "is-promise": "^2.0.0",
19 | "setimmediate": "^1.0.2"
20 | },
21 | "author": "Michael Ridgway ",
22 | "contributors": [],
23 | "licenses": [
24 | {
25 | "type": "BSD-3-Clause",
26 | "url": "https://github.com/yahoo/fluxible/blob/master/LICENSE.md"
27 | }
28 | ],
29 | "keywords": [
30 | "yahoo",
31 | "flux",
32 | "react",
33 | "fluxible",
34 | "isomorphic"
35 | ]
36 | }
37 |
--------------------------------------------------------------------------------
/packages/generator-fluxible/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "generator-fluxible",
3 | "version": "1.2.0",
4 | "description": "Yeoman generator for Fluxible",
5 | "main": "app/index.js",
6 | "repository": "yahoo/fluxible",
7 | "author": {
8 | "name": "Seth Bertalotto",
9 | "email": "sbertal@yahoo-inc.com"
10 | },
11 | "engines": {
12 | "node": ">=0.10.0"
13 | },
14 | "scripts": {
15 | "cover": "../../node_modules/.bin/istanbul cover --dir artifacts -- ../../node_modules/.bin/_mocha test --recursive --reporter spec",
16 | "lint": "../../node_modules/.bin/eslint app/ test/",
17 | "test": "../../node_modules/.bin/mocha tests/unit --recursive --reporter=spec"
18 | },
19 | "precommit": [
20 | "lint",
21 | "test"
22 | ],
23 | "files": [
24 | "app"
25 | ],
26 | "keywords": [
27 | "yeoman-generator",
28 | "flux",
29 | "react",
30 | "fluxible"
31 | ],
32 | "dependencies": {
33 | "chalk": "^1.0.0",
34 | "underscore.string": "^3.0.2",
35 | "yeoman-generator": "^0.20.1",
36 | "yosay": "^1.0.2"
37 | },
38 | "peerDependencies": {
39 | "yo": ">=1.0.0"
40 | },
41 | "devDependencies": {
42 | "yo": ">=1.0.0"
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/docs/community/articles.md:
--------------------------------------------------------------------------------
1 | # Articles and Blogs
2 |
3 | Take a look at some of the articles that our community has written. Edit this
4 | page on GitHub to list yours!
5 |
6 | * 2015-09-24 - [How to Migrate React to Isomorphic Rendering](https://medium.com/building-coursera/how-to-migrate-react-to-isomorphic-rendering-88347ba653a5)
7 | * 2015-08-27 - [Building a Site Using Isomorphic Flux with Fluxible](http://jacksoncmorgan.com/blog/fluxiblemaster)
8 | * 2015-06-10 - [yahoo/fluxible による SPA + Server Rendering の概観](http://havelog.ayumusato.com/develop/javascript/e675-spa_and_server_rendering_with_fluxible.html)
9 | * 2015-04-14 - [Managing Context In a Fluxible App Using React-Router](http://www.ian-thomas.net/managing-context-in-a-fluxible-app-using-react-router/)
10 | * 2015-03-12 - [How I use fluxible with Hapi](http://danecando.com/how-i-use-fluxible-with-hapi/)
11 | * 2015-02-20 - [Exploring Isomorphic JavaScript](http://nicolashery.com/exploring-isomorphic-javascript/)
12 | * 2014-11-30 - [Isomorphic React + Flux using Yahoo's Fluxible](http://dev.alexishevia.com/2014/11/isomorphic-react-flux-using-yahoos.html)
13 | * 2014-11-06 - [Bringing Flux to the Server](/blog/2014-11-06-bringing-flux-to-the-server.md)
14 |
--------------------------------------------------------------------------------
/packages/fluxible/tests/fixtures/plugins/DimensionsContextPlugin.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2014, Yahoo! Inc.
3 | * Copyrights licensed under the New BSD License. See the accompanying LICENSE file for terms.
4 | */
5 | 'use strict';
6 |
7 | module.exports = function (dims) {
8 | var dimensions = dims;
9 | return {
10 | name: 'DimensionsPlugin',
11 | plugActionContext: function (actionContext) {
12 | actionContext.getDimensions = function () {
13 | return dimensions;
14 | };
15 | },
16 | plugComponentContext: function (componentContext) {
17 | componentContext.getDimensions = function () {
18 | return dimensions;
19 | };
20 | },
21 | plugStoreContext: function (storeContext) {
22 | storeContext.getDimensions = function () {
23 | return dimensions;
24 | };
25 | },
26 | dehydrate: function () {
27 | return {
28 | dimensions: dimensions
29 | };
30 | },
31 | rehydrate: function (state, done) {
32 | dimensions = state.dimensions;
33 | setImmediate(done);
34 | }
35 | }
36 | };
37 |
--------------------------------------------------------------------------------
/packages/fluxible-addons-react/createElementWithContext.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2015, Yahoo Inc.
3 | * Copyrights licensed under the New BSD License. See the accompanying LICENSE file for terms.
4 | */
5 | 'use strict';
6 |
7 | var React = require('react');
8 | var FluxibleComponent = require('./FluxibleComponent');
9 |
10 | /**
11 | * Creates an instance of the app level component with given props and a proper component
12 | * context.
13 | * @param {FluxibleContext} fluxibleContext
14 | * @param {Object} props
15 | * @return {ReactElement}
16 | */
17 | module.exports = function createElement(fluxibleContext, props) {
18 | var Component = fluxibleContext.getComponent();
19 | if (!Component) {
20 | throw new Error('A top-level component was not passed to the Fluxible' +
21 | ' constructor.');
22 | }
23 | if (Component.displayName && -1 !== Component.displayName.indexOf('ContextProvider')) {
24 | return React.createElement(Component, Object.assign({}, {
25 | context: fluxibleContext.getComponentContext()
26 | }, props));
27 | }
28 | var componentInstance = React.createElement(Component, props);
29 | return React.createElement(FluxibleComponent, {
30 | context: fluxibleContext.getComponentContext()
31 | }, componentInstance);
32 | };
33 |
--------------------------------------------------------------------------------
/packages/fluxible/tests/fixtures/plugins/DimensionsContextPluginPromise.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2014, Yahoo! Inc.
3 | * Copyrights licensed under the New BSD License. See the accompanying LICENSE file for terms.
4 | */
5 | 'use strict';
6 |
7 | module.exports = function (dims) {
8 | var dimensions = dims;
9 | return {
10 | name: 'DimensionsPlugin',
11 | plugActionContext: function (actionContext) {
12 | actionContext.getDimensions = function () {
13 | return dimensions;
14 | };
15 | },
16 | plugComponentContext: function (componentContext) {
17 | componentContext.getDimensions = function () {
18 | return dimensions;
19 | };
20 | },
21 | plugStoreContext: function (storeContext) {
22 | storeContext.getDimensions = function () {
23 | return dimensions;
24 | };
25 | },
26 | dehydrate: function () {
27 | return {
28 | dimensions: dimensions
29 | };
30 | },
31 | rehydrate: function (state) {
32 | return new Promise(function (resolve) {
33 | dimensions = state.dimensions;
34 | resolve();
35 | });
36 | }
37 | }
38 | };
39 |
--------------------------------------------------------------------------------
/packages/generator-fluxible/app/templates/components/Application.js:
--------------------------------------------------------------------------------
1 | /*globals document*/
2 |
3 | import React from 'react';
4 | import Nav from './Nav';
5 | import ApplicationStore from '../stores/ApplicationStore';
6 | import { connectToStores, provideContext } from 'fluxible-addons-react';
7 | import { handleHistory } from 'fluxible-router';
8 | import pages from '../configs/routes';
9 |
10 | class Application extends React.Component {
11 | render() {
12 | var Handler = this.props.currentRoute.get('handler');
13 |
14 | return (
15 |
16 |
17 |
18 |
19 | );
20 | }
21 |
22 | componentDidUpdate(prevProps, prevState) {
23 | const newProps = this.props;
24 | if (newProps.pageTitle === prevProps.pageTitle) {
25 | return;
26 | }
27 | document.title = newProps.pageTitle;
28 | }
29 | }
30 |
31 | export default provideContext(handleHistory(connectToStores(
32 | Application,
33 | [ApplicationStore],
34 | function (context, props) {
35 | var appStore = context.getStore(ApplicationStore);
36 | return {
37 | pageTitle: appStore.getPageTitle()
38 | };
39 | }
40 | )));
41 |
--------------------------------------------------------------------------------
/packages/generator-fluxible/app/templates/webpack.config.production.js:
--------------------------------------------------------------------------------
1 | var webpack = require('webpack');
2 | var path = require('path');
3 |
4 | var webpackConfig = {
5 | resolve: {
6 | extensions: ['', '.js']
7 | },
8 | entry: [
9 | './client.js'
10 | ],
11 | output: {
12 | path: path.resolve('./build/js'),
13 | publicPath: '/public/js/',
14 | filename: 'main.min.js'
15 | },
16 | module: {
17 | loaders: [
18 | {
19 | test: /\.(js|jsx)$/,
20 | exclude: /node_modules/,
21 | loaders: [
22 | require.resolve('babel-loader')
23 | ]
24 | },
25 | { test: /\.json$/, loader: 'json-loader'}
26 | ]
27 | },
28 | node: {
29 | setImmediate: false
30 | },
31 | plugins: [
32 | new webpack.DefinePlugin({
33 | 'process.env': {
34 | NODE_ENV: JSON.stringify('production')
35 | }
36 | }),
37 | new webpack.optimize.DedupePlugin(),
38 | new webpack.optimize.UglifyJsPlugin({
39 | compress: {
40 | warnings: false
41 | }
42 | })
43 | ],
44 | devtool: 'source-map'
45 | };
46 |
47 | module.exports = webpackConfig;
48 |
--------------------------------------------------------------------------------
/packages/generator-fluxible/app/templates/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "<%= name %>",
3 | "version": "0.0.0",
4 | "private": true,
5 | "main": "start.js",
6 | "scripts": {
7 | "build": "webpack & webpack --config webpack.config.production.js",
8 | "dev": "node webpack-dev-server.js",
9 | "lint": "eslint ./*.js ./**/*.js",
10 | "start": "node start.js"
11 | },
12 | "dependencies": {
13 | "babel": "^5.8.23",
14 | "babel-core": "^5.8.25",
15 | "body-parser": "^1.6.4",
16 | "compression": "^1.5.1",
17 | "cookie-parser": "^1.3.3",
18 | "csurf": "^1.6.3",
19 | "debug": "^2.0.0",
20 | "express": "^4.3.2",
21 | "express-state": "^1.2.0",
22 | "fluxible": "^1.0.0",
23 | "fluxible-addons-react": "^0.2.0",
24 | "fluxible-plugin-fetchr": "^0.3.0",
25 | "fluxible-router": "^0.3.0",
26 | "react": "^0.14.0",
27 | "react-dom": "^0.14.0",
28 | "serialize-javascript": "^1.0.0",
29 | "serve-favicon": "^2.1.6"
30 | },
31 | "devDependencies": {
32 | "babel-eslint": "^3.0.1",
33 | "babel-loader": "^5.1.3",
34 | "bundle-loader": "^0.5.0",
35 | "eslint": "^0.24.0",
36 | "json-loader": "^0.5.1",
37 | "nodemon": "^1.2.1",
38 | "react-hot-loader": "^1.2.8",
39 | "shelljs": "^0.5.3",
40 | "webpack": "^1.4.12",
41 | "webpack-dev-server": "^1.6.5"
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/packages/fluxible-addons-react/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Change Log
2 |
3 | ## 0.2.0
4 |
5 | ### Breaking Changes
6 |
7 | * Upgraded to React 0.14, breaking compatibility with older versions of React
8 | * Removed dependency on `Object.assign` library, must be polyfilled
9 |
10 | ## 0.1.8
11 |
12 | ### Bug Fixes
13 |
14 | * Expose batchUpdatePlugin from default exports
15 |
16 | ## 0.1.7
17 |
18 | ### Features
19 |
20 | * [#21] Add batch update plugin that will batch all setState calls within the same dispatch
21 |
22 | ### Internal
23 |
24 | * [#22] Updated object-assign dependency
25 | * [#23] Updated dev dependencies
26 |
27 |
28 | ## 0.1.6
29 |
30 | ### Bug Fixes
31 |
32 | * [#9] Fixes calling stateGetter when component receives new props
33 |
34 | ## 0.1.5
35 |
36 | ### Bug Fixes
37 |
38 | * [#10] Update fluxible dep and remove unused dep
39 |
40 | ## 0.1.4
41 |
42 | ### Bug Fixes
43 |
44 | * Update fluxible version
45 |
46 | ## 0.1.3
47 |
48 | ### Bug Fixes
49 |
50 | * Allow pre-release versions of fluxible
51 |
52 | ## 0.1.2
53 |
54 | ### Bug Fixes
55 |
56 | * [#2] Remove circular dependency with fluxible
57 | * [#3] Add backwards dependency for old connectToStores with deprecation notices
58 | * [#4] Fix documentation links
59 |
60 | ## 0.1.1
61 |
62 | ### Bug Fixes
63 |
64 | * [#1] Loosen fluxible dependency
65 |
66 | ## 0.1.0
67 |
68 | * Init commit
69 |
--------------------------------------------------------------------------------
/packages/generator-fluxible/app/templates/webpack.config.js:
--------------------------------------------------------------------------------
1 | var webpack = require('webpack');
2 | var path = require('path');
3 |
4 | var webpackConfig = {
5 | resolve: {
6 | extensions: ['', '.js', '.jsx']
7 | },
8 | entry: [
9 | 'webpack-dev-server/client?http://localhost:3000',
10 | 'webpack/hot/only-dev-server',
11 | './client.js'
12 | ],
13 | output: {
14 | path: path.resolve('./build/js'),
15 | publicPath: '/public/js/',
16 | filename: 'main.js'
17 | },
18 | module: {
19 | loaders: [
20 | {
21 | test: /\.(js|jsx)$/,
22 | exclude: /node_modules/,
23 | loaders: [
24 | require.resolve('react-hot-loader'),
25 | require.resolve('babel-loader')
26 | ]
27 | },
28 | { test: /\.json$/, loader: 'json-loader'}
29 | ]
30 | },
31 | node: {
32 | setImmediate: false
33 | },
34 | plugins: [
35 | new webpack.HotModuleReplacementPlugin(),
36 | new webpack.NoErrorsPlugin(),
37 | new webpack.DefinePlugin({
38 | 'process.env': {
39 | NODE_ENV: JSON.stringify(process.env.NODE_ENV)
40 | }
41 | })
42 | ],
43 | devtool: 'eval'
44 | };
45 |
46 | module.exports = webpackConfig;
47 |
--------------------------------------------------------------------------------
/packages/fluxible-addons-react/batchedUpdatePlugin.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2014, Yahoo! Inc.
3 | * Copyrights licensed under the New BSD License. See the accompanying LICENSE file for terms.
4 | */
5 | 'use strict';
6 | var batchedUpdates = require('react-dom').unstable_batchedUpdates;
7 |
8 | module.exports = function createBatchedUpdatePlugin(options) {
9 | /**
10 | * @class BatchedUpdatePlugin
11 | */
12 | return {
13 | name: 'BatchedUpdatePlugin',
14 | /**
15 | * Called to plug the FluxContext
16 | * @method plugContext
17 | * @returns {Object}
18 | */
19 | plugContext: function plugContext() {
20 | return {
21 | /**
22 | * Provides full access to the router in the action context
23 | * @param {Object} actionContext
24 | */
25 | plugActionContext: function plugActionContext(actionContext) {
26 | var oldDispatch = actionContext.dispatch;
27 | actionContext.dispatch = function () {
28 | var args = arguments;
29 | batchedUpdates(function () {
30 | oldDispatch.apply(actionContext, args);
31 | });
32 | };
33 | }
34 | };
35 | }
36 | };
37 | };
38 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "fluxible-repo",
3 | "version": "1.0.0",
4 | "private": true,
5 | "description": "Fluxible monorepo",
6 | "repository": {
7 | "type": "git",
8 | "url": "https://github.com/yahoo/fluxible"
9 | },
10 | "scripts": {
11 | "test": "mocha packages/*/tests/unit/ --recursive --compilers js:babel/register --reporter spec",
12 | "cover": "istanbul cover --dir artifacts -- _mocha packages/*/tests/unit/ --recursive --compilers js:babel/register --reporter spec",
13 | "lint": "eslint *.js",
14 | "postinstall": "gulp install",
15 | "preversion": "echo \"Error: version should be called using gulp\" && exit 1"
16 | },
17 | "dependencies": {},
18 | "devDependencies": {
19 | "async": "^1.4.2",
20 | "babel": "^5.8.23",
21 | "babel-core": "^5.8.25",
22 | "babel-eslint": "^4.1.0",
23 | "chai": "^3.2.0",
24 | "coveralls": "^2.11.1",
25 | "eslint": "^1.3.0",
26 | "eslint-plugin-react": "^3.3.0",
27 | "gulp": "^3.9.0",
28 | "istanbul": "^0.3.2",
29 | "jsdom": "^3.1.2",
30 | "mocha": "^2.0.1",
31 | "mockery": "^1.4.0",
32 | "pre-commit": "^1.0.7",
33 | "react": "^0.14.0",
34 | "react-addons-test-utils": "^0.14.0",
35 | "react-dom": "^0.14.0",
36 | "shelljs": "^0.5.3",
37 | "yargs": "^3.27.0"
38 | },
39 | "pre-commit": [
40 | "lint",
41 | "test"
42 | ],
43 | "author": "Michael Ridgway ",
44 | "license": "BSD-3-Clause"
45 | }
46 |
--------------------------------------------------------------------------------
/packages/fluxible/docs/api/addons/BaseStore.md:
--------------------------------------------------------------------------------
1 | # BaseStore
2 |
3 | ```js
4 | import BaseStore from 'fluxible/addons/BaseStore';
5 | ```
6 |
7 | A base class that you can extend to reduce boilerplate when creating stores.
8 |
9 | ## Built-In Methods
10 |
11 | * `emitChange()` - emits a 'change' event
12 | * `getContext()` - returns the [store context](../FluxibleContext.md#store-context)
13 | * `addChangeListener(callback)` - simple method to add a change listener
14 | * `removeChangeListener(callback)` - removes a change listener
15 | * `shouldDehydrate()` - default implementation that returns true if a `change` event has been emitted
16 |
17 | ## Example
18 |
19 | ```js
20 | class ApplicationStore extends BaseStore {
21 | constructor (dispatcher) {
22 | super(dispatcher);
23 | this.currentPageName = null;
24 | }
25 |
26 | handleReceivePage (payload) {
27 | this.currentPageName = payload.pageName;
28 | this.emitChange();
29 | }
30 |
31 | getCurrentPageName () {
32 | return this.currentPageName;
33 | }
34 |
35 | // For sending state to the client
36 | dehydrate () {
37 | return {
38 | currentPageName: this.currentPageName
39 | };
40 | }
41 |
42 | // For rehydrating server state
43 | rehydrate (state) {
44 | this.currentPageName = state.currentPageName;
45 | }
46 | }
47 |
48 | ApplicationStore.storeName = 'ApplicationStore';
49 | ApplicationStore.handlers = {
50 | 'RECEIVE_PAGE': 'handleReceivePage'
51 | };
52 |
53 | export default ApplicationStore;
54 | ```
55 |
--------------------------------------------------------------------------------
/docs/community/presentations.md:
--------------------------------------------------------------------------------
1 | # Presentations
2 |
3 | Take a look at talks and presentations we (Yahoo) and the community have done.
4 |
5 | ### Isomorphic Flux - ReactEurope 2015 - Michael Ridgway
6 |
7 | Michael Ridgway presented "Isomorphic Flux" at the [ReactEurope](http://www.react-europe.org/) conference 2015.
8 |
9 |
10 |
11 | ### Flux Panel - ReactConf 2015 - Michael Ridgway
12 |
13 | Our own, [Michael Ridgway](https://twitter.com/theridgway), was part of a panel of Flux experts at ReactConf 2015.
14 |
15 |
16 |
17 | ### Isomorphic and Reactive Applications - NodeSummit 2015 - Peter Marton
18 |
19 | Peter Marton from RisingStack gave a [great overview](https://speakerdeck.com/slashdotpeter/nodesummit-isomorphic-and-reactive-applications) of building isomorphic React applications with Fluxible's architecture.
20 |
21 |
22 |
23 | ### Isomorphic Flux - SF React Meetup, November 2014 - Michael Ridgway
24 |
25 | Presentation of the Fluxible architecture at the SF React Meetup.
26 |
27 |
28 |
--------------------------------------------------------------------------------
/packages/fluxible-reducer-store/README.md:
--------------------------------------------------------------------------------
1 | # Fluxible Reducer Store
2 |
3 | [](http://badge.fury.io/js/fluxible-reducer-store)
4 |
10 |
11 | A helper method for creating reducer stores for Fluxible.
12 |
13 | ```bash
14 | $ npm install --save fluxible-reducer-store
15 | ```
16 |
17 | ## Docs
18 |
19 | * [API](https://github.com/yahoo/fluxible/blob/master/packages/fluxible-reducer-store/docs/api/createReducerStore.md)
20 |
21 | ## Browser Compatibility
22 |
23 | Fluxible is written with ES2015 in mind and should be used along with polyfills
24 | for features like [`Object.assign`][objectAssign] in order to support all
25 | browsers. We recommend using [Babel][babel].
26 |
27 | ## License
28 |
29 | This software is free to use under the Yahoo Inc. BSD license.
30 | See the [LICENSE file][] for license text and copyright information.
31 |
32 | [Promise]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise
33 | [objectAssign]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign
34 | [babel]: https://babeljs.io/
35 | [LICENSE file]: https://github.com/yahoo/fluxible/blob/master/LICENSE.md
36 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | Software License Agreement (BSD License)
2 | ========================================
3 |
4 | Copyright (c) 2015, Yahoo Inc. All rights reserved.
5 | ----------------------------------------------------
6 |
7 | Redistribution and use of this software in source and binary forms, with or
8 | without modification, are permitted provided that the following conditions are
9 | met:
10 |
11 | * Redistributions of source code must retain the above copyright notice, this
12 | list of conditions and the following disclaimer.
13 | * Redistributions in binary form must reproduce the above copyright notice,
14 | this list of conditions and the following disclaimer in the documentation
15 | and/or other materials provided with the distribution.
16 | * Neither the name of Yahoo Inc. nor the names of this package's contributors
17 | may be used to endorse or promote products derived from this software
18 | without specific prior written permission of Yahoo Inc.
19 |
20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
21 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
22 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
24 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
25 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
26 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
27 | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
29 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 |
--------------------------------------------------------------------------------
/packages/fluxible-addons-react/docs/api/FluxibleComponent.md:
--------------------------------------------------------------------------------
1 | # FluxibleComponent
2 |
3 | ```js
4 | import FluxibleComponent from 'fluxible-addons-react/FluxibleComponent';
5 | ```
6 |
7 | The `FluxibleComponent` is a wrapper component that will provide all of its children with access to the Fluxible component
8 | context via React's `childContextTypes` and `getChildContext`. This should be used to wrap your top level component. It provides access to the methods on the [component context](../../../../packages/fluxible/docs/api/Components.md#component-context).
9 |
10 | You can get access to these methods by setting the correct `contextTypes` within your component or including the [`FluxibleMixin`](./FluxibleMixin.md) which will add them for you.
11 |
12 | ## Usage
13 |
14 | If you have a component that needs access to the [`ComponentContext`](../../../../packages/fluxible/docs/api/Components.md#component-context) methods:
15 |
16 | ```js
17 | class Component extends React.Component {
18 | constructor(props) {
19 | super(props);
20 | this.state = this.context.getStore(FooStore).getState();
21 | }
22 | }
23 | Component.contextTypes = {
24 | getStore: React.PropTypes.func.isRequired,
25 | executeAction: React.PropTypes.func.isRequired
26 | };
27 | ```
28 |
29 | you can wrap the component with `FluxibleComponent` to provide the correct context:
30 |
31 | ```js
32 | let html = ReactDOM.renderToString(
33 |
34 |
35 |
36 | );
37 | ```
38 |
39 | If you are using [`createElementWithContext`](./createElementWithContext.md) this will happen for you automatically:
40 |
41 | ```js
42 | let html = ReactDOM.renderToString(createElementWithContext(context));
43 | ```
44 |
--------------------------------------------------------------------------------
/packages/fluxible/LICENSE.md:
--------------------------------------------------------------------------------
1 | Software License Agreement (BSD License)
2 | ========================================
3 |
4 | Copyright (c) 2015, Yahoo Inc. All rights reserved.
5 | ----------------------------------------------------
6 |
7 | Redistribution and use of this software in source and binary forms, with or
8 | without modification, are permitted provided that the following conditions are
9 | met:
10 |
11 | * Redistributions of source code must retain the above copyright notice, this
12 | list of conditions and the following disclaimer.
13 | * Redistributions in binary form must reproduce the above copyright notice,
14 | this list of conditions and the following disclaimer in the documentation
15 | and/or other materials provided with the distribution.
16 | * Neither the name of Yahoo Inc. nor the names of this package's contributors
17 | may be used to endorse or promote products derived from this software
18 | without specific prior written permission of Yahoo Inc.
19 |
20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
21 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
22 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
24 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
25 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
26 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
27 | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
29 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 |
--------------------------------------------------------------------------------
/packages/fluxible-addons-react/LICENSE.md:
--------------------------------------------------------------------------------
1 | Software License Agreement (BSD License)
2 | ========================================
3 |
4 | Copyright (c) 2015, Yahoo Inc. All rights reserved.
5 | ----------------------------------------------------
6 |
7 | Redistribution and use of this software in source and binary forms, with or
8 | without modification, are permitted provided that the following conditions are
9 | met:
10 |
11 | * Redistributions of source code must retain the above copyright notice, this
12 | list of conditions and the following disclaimer.
13 | * Redistributions in binary form must reproduce the above copyright notice,
14 | this list of conditions and the following disclaimer in the documentation
15 | and/or other materials provided with the distribution.
16 | * Neither the name of Yahoo Inc. nor the names of this package's contributors
17 | may be used to endorse or promote products derived from this software
18 | without specific prior written permission of Yahoo Inc.
19 |
20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
21 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
22 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
24 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
25 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
26 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
27 | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
29 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 |
--------------------------------------------------------------------------------
/packages/fluxible-reducer-store/LICENSE.md:
--------------------------------------------------------------------------------
1 | Software License Agreement (BSD License)
2 | ========================================
3 |
4 | Copyright (c) 2015, Yahoo Inc. All rights reserved.
5 | ----------------------------------------------------
6 |
7 | Redistribution and use of this software in source and binary forms, with or
8 | without modification, are permitted provided that the following conditions are
9 | met:
10 |
11 | * Redistributions of source code must retain the above copyright notice, this
12 | list of conditions and the following disclaimer.
13 | * Redistributions in binary form must reproduce the above copyright notice,
14 | this list of conditions and the following disclaimer in the documentation
15 | and/or other materials provided with the distribution.
16 | * Neither the name of Yahoo Inc. nor the names of this package's contributors
17 | may be used to endorse or promote products derived from this software
18 | without specific prior written permission of Yahoo Inc.
19 |
20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
21 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
22 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
24 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
25 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
26 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
27 | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
29 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 |
--------------------------------------------------------------------------------
/packages/generator-fluxible/LICENSE.md:
--------------------------------------------------------------------------------
1 | Software License Agreement (BSD License)
2 | ========================================
3 |
4 | Copyright (c) 2015, Yahoo Inc. All rights reserved.
5 | ----------------------------------------------------
6 |
7 | Redistribution and use of this software in source and binary forms, with or
8 | without modification, are permitted provided that the following conditions are
9 | met:
10 |
11 | * Redistributions of source code must retain the above copyright notice, this
12 | list of conditions and the following disclaimer.
13 | * Redistributions in binary form must reproduce the above copyright notice,
14 | this list of conditions and the following disclaimer in the documentation
15 | and/or other materials provided with the distribution.
16 | * Neither the name of Yahoo Inc. nor the names of this package's contributors
17 | may be used to endorse or promote products derived from this software
18 | without specific prior written permission of Yahoo Inc.
19 |
20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
21 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
22 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
24 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
25 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
26 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
27 | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
29 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 |
--------------------------------------------------------------------------------
/packages/fluxible-reducer-store/tests/fixtures/data/messages.js:
--------------------------------------------------------------------------------
1 | module.exports = [
2 | {
3 | id: 'm_1',
4 | threadID: 't_1',
5 | threadName: 'Jing and Bill',
6 | authorName: 'Bill',
7 | text: 'Hey Jing, want to give a Flux talk at ForwardJS?',
8 | timestamp: Date.now() - 99999
9 | },
10 | {
11 | id: 'm_2',
12 | threadID: 't_1',
13 | threadName: 'Jing and Bill',
14 | authorName: 'Bill',
15 | text: 'Seems like a pretty cool conference.',
16 | timestamp: Date.now() - 89999
17 | },
18 | {
19 | id: 'm_3',
20 | threadID: 't_1',
21 | threadName: 'Jing and Bill',
22 | authorName: 'Jing',
23 | text: 'Sounds good. Will they be serving dessert?',
24 | timestamp: Date.now() - 79999
25 | },
26 | {
27 | id: 'm_4',
28 | threadID: 't_2',
29 | threadName: 'Dave and Bill',
30 | authorName: 'Bill',
31 | text: 'Hey Dave, want to get a beer after the conference?',
32 | timestamp: Date.now() - 69999
33 | },
34 | {
35 | id: 'm_5',
36 | threadID: 't_2',
37 | threadName: 'Dave and Bill',
38 | authorName: 'Dave',
39 | text: 'Totally! Meet you at the hotel bar.',
40 | timestamp: Date.now() - 59999
41 | },
42 | {
43 | id: 'm_6',
44 | threadID: 't_3',
45 | threadName: 'Functional Heads',
46 | authorName: 'Bill',
47 | text: 'Hey Brian, are you going to be talking about functional stuff?',
48 | timestamp: Date.now() - 49999
49 | },
50 | {
51 | id: 'm_7',
52 | threadID: 't_3',
53 | threadName: 'Bill and Brian',
54 | authorName: 'Brian',
55 | text: 'At ForwardJS? Yeah, of course. See you there!',
56 | timestamp: Date.now() - 39999
57 | }
58 | ];
59 |
--------------------------------------------------------------------------------
/packages/fluxible/tests/fixtures/plugins/TestApplicationPluginSync.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2014, Yahoo! Inc.
3 | * Copyrights licensed under the New BSD License. See the accompanying LICENSE file for terms.
4 | */
5 | 'use strict';
6 |
7 | module.exports = function TestApplicationPlugin(initialFoo, initialBar) {
8 | var foo = initialFoo;
9 | var bar = initialBar;
10 | return {
11 | name: 'TestAppPlugin',
12 | plugContext: function (options) {
13 | return {
14 | plugActionContext: function plugActionContext(actionContext) {
15 | actionContext.getFoo = function () {
16 | return foo;
17 | };
18 | actionContext.getBar = function () {
19 | return bar;
20 | };
21 | },
22 | plugComponentContext: function plugComponentContext(componentContext) {
23 | componentContext.getFoo = function () {
24 | return foo;
25 | };
26 | },
27 | plugStoreContext: function plugStoreContext(storeContext) {
28 | storeContext.getFoo = function () {
29 | return foo;
30 | };
31 | },
32 | dehydrate: function () {
33 | return {
34 | bar: bar
35 | };
36 | },
37 | rehydrate: function (state) {
38 | bar = state.bar;
39 | }
40 | };
41 | },
42 | dehydrate: function dehydrate() {
43 | return {
44 | foo: foo
45 | };
46 | },
47 | rehydrate: function rehydrate(state) {
48 | foo = state.foo;
49 | }
50 | }
51 | };
52 |
--------------------------------------------------------------------------------
/packages/fluxible-addons-react/docs/api/FluxibleMixin.md:
--------------------------------------------------------------------------------
1 | # FluxibleMixin
2 |
3 | ***FluxibleMixin will be deprecated since React mixin support will eventually
4 | be removed. It is highly recommended to use [provideContext](provideContext.md)
5 | instead.***
6 |
7 | ```js
8 | var FluxibleMixin = require('fluxible-addons-react/FluxibleMixin');
9 | ```
10 |
11 | The mixin will add the `contextTypes` `getStore` and `executeAction`
12 | to your component.
13 |
14 | The mixin can also be used to statically list store dependencies and listen to
15 | them automatically in `componentDidMount`. This is done by adding a static
16 | property `storeListeners` in your component.
17 |
18 | You can do this with an array, which will default all store listeners to call
19 | the `onChange` method:
20 |
21 | ```js
22 | var FooStore = require('./stores/FooStore'); // Your store
23 | var Component = React.createClass({
24 | mixins: [FluxibleMixin],
25 | statics: {
26 | storeListeners: [FooStore]
27 | },
28 | onChange: function () {
29 | this.setState(this.getStore(FooStore).getState());
30 | },
31 | });
32 | ```
33 |
34 | Or you can be more explicit with which function to call for each store by using a hash:
35 |
36 | ```js
37 | var FooStore = require('./stores/FooStore'); // Your store
38 | var BarStore = require('./stores/BarStore'); // Your store
39 | var Component = React.createClass({
40 | mixins: [FluxibleMixin],
41 | statics: {
42 | storeListeners: {
43 | onFooStoreChange: [FooStore],
44 | onBarStoreChange: [BarStore]
45 | }
46 | },
47 | onFooStoreChange: function () {
48 | this.setState(this.getStore(FooStore).getState());
49 | },
50 | onBarStateChange: function () {
51 | this.setState(this.getStore(BarStore).getState());
52 | }
53 | });
54 | ```
55 |
56 | This prevents boilerplate for listening to stores in `componentDidMount` and
57 | unlistening in `componentWillUnmount`.
58 |
--------------------------------------------------------------------------------
/packages/fluxible/tests/fixtures/plugins/TestApplicationPlugin.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2014, Yahoo! Inc.
3 | * Copyrights licensed under the New BSD License. See the accompanying LICENSE file for terms.
4 | */
5 | 'use strict';
6 |
7 | module.exports = function TestApplicationPlugin(initialFoo, initialBar) {
8 | var foo = initialFoo;
9 | var bar = initialBar;
10 | return {
11 | name: 'TestAppPlugin',
12 | plugContext: function (options) {
13 | return {
14 | plugActionContext: function plugActionContext(actionContext) {
15 | actionContext.getFoo = function () {
16 | return foo;
17 | };
18 | actionContext.getBar = function () {
19 | return bar;
20 | };
21 | },
22 | plugComponentContext: function plugComponentContext(componentContext) {
23 | componentContext.getFoo = function () {
24 | return foo;
25 | };
26 | },
27 | plugStoreContext: function plugStoreContext(storeContext) {
28 | storeContext.getFoo = function () {
29 | return foo;
30 | };
31 | },
32 | dehydrate: function () {
33 | return {
34 | bar: bar
35 | };
36 | },
37 | rehydrate: function (state, done) {
38 | bar = state.bar;
39 | setImmediate(done);
40 | }
41 | };
42 | },
43 | dehydrate: function dehydrate() {
44 | return {
45 | foo: foo
46 | };
47 | },
48 | rehydrate: function rehydrate(state, done) {
49 | foo = state.foo;
50 | setImmediate(done);
51 | }
52 | }
53 | };
54 |
--------------------------------------------------------------------------------
/packages/fluxible/UPGRADE.md:
--------------------------------------------------------------------------------
1 | # Upgrade Guide
2 |
3 | ## `1.0.0`
4 |
5 | ### React addons removed from Fluxible
6 |
7 | All previously deprecated React integrations have been removed. See notes
8 | from `0.5.0` on migration to `fluxible-addons-react`. Additionally,
9 | `require('fluxible').contextTypes` has been removed and will be part of
10 | `fluxible-addons-react`.
11 |
12 | ### `componentActionHandler` renamed to `componentActionErrorHandler`
13 |
14 | This has been renamed to reflect that it is only called when an error occurs.
15 |
16 | ### Promise library dependency removed
17 |
18 | In keeping up-to-date with ES2015, the dependency on `es6-promise` and
19 | `object-assign` libraries have been removed. Users should now add their
20 | own polyfills to their application in browsers that do not support
21 | these features yet. We recommend using [Babel](https://babeljs.io/).
22 |
23 | ## `0.5.0`
24 |
25 | ### React addons moved to fluxible-addons-react
26 |
27 | The React addons have been broken out into a separate package called [fluxible-addons-react](https://github.com/yahoo/fluxible-addons-react). This was done to faciliate the use of Fluxible with other view libraries like React Native.
28 |
29 | Your application should depend on `fluxible-addons-react` directly and require the addons from that package.
30 |
31 | Note the new way to requre these with es6 uses curly braces:
32 |
33 | ```js
34 | import {connectToStores} from 'fluxible-addons-react';
35 | ```
36 |
37 | ### `connectToStores`'s `getStateFromStores` signature has changed to `(context, props)`
38 |
39 | Your `connectToStores` should change from:
40 |
41 | ```js
42 | connectToStores(Component, [MyStore], (stores, props) => {
43 | return {
44 | foo: stores.MyStore.getFoo()
45 | };
46 | });
47 | ```
48 |
49 | to
50 |
51 | ```js
52 | connectToStores(Component, [MyStore], (context, props) => {
53 | return {
54 | foo: context.getStore(MyStore).getFoo()
55 | };
56 | });
57 | ```
58 |
--------------------------------------------------------------------------------
/packages/generator-fluxible/README.md:
--------------------------------------------------------------------------------
1 | # generator-fluxible
2 |
3 | [](http://badge.fury.io/js/generator-fluxible)
4 |
9 |
10 | ## Getting Started
11 |
12 | ```bash
13 | npm install -g yo
14 | ```
15 |
16 | To install generator-fluxible from npm, run:
17 |
18 | ```bash
19 | npm install -g generator-fluxible
20 | ```
21 |
22 | Finally, initiate the generator:
23 |
24 | ```bash
25 | yo fluxible
26 | ```
27 |
28 | During development, execute `npm run dev` to initiate webpack-dev-server
29 | (with react-hot-loader support) and your application's server using nodemon.
30 | Browse to `http://localhost:3000` to see a very simple Fluxible site with
31 | server-side rendering and client-side navigation. When you change files,
32 | the client will be hot-reloaded (with the exception of stores) and your
33 | application server will restart so that you can see the server-side changes
34 | on the next refresh.
35 |
36 | For other environments, make sure your application is built using
37 | `npm run build` and then run `npm start`.
38 |
39 | ## Debugging
40 |
41 | Fluxible uses [debug](https://www.npmjs.com/package/debug) to expose debugging
42 | information on the server and client.
43 |
44 | ### Server
45 |
46 | Start the application with the `DEBUG` environment variable: `DEBUG=* grunt`.
47 |
48 | ### Client
49 |
50 | `fluxibleDebug` is exposed to the `window` object to manage debugging. You can
51 | enable it via the browser console: `fluxibleDebug.enable('*');` then refresh
52 | the page. To disable, type the following: `fluxibleDebug.disable();`.
53 |
--------------------------------------------------------------------------------
/packages/fluxible/tests/fixtures/plugins/TestApplicationPluginPromise.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2014, Yahoo! Inc.
3 | * Copyrights licensed under the New BSD License. See the accompanying LICENSE file for terms.
4 | */
5 | 'use strict';
6 |
7 | module.exports = function TestApplicationPlugin(initialFoo, initialBar) {
8 | var foo = initialFoo;
9 | var bar = initialBar;
10 | return {
11 | name: 'TestAppPlugin',
12 | plugContext: function (options) {
13 | return {
14 | plugActionContext: function plugActionContext(actionContext) {
15 | actionContext.getFoo = function () {
16 | return foo;
17 | };
18 | actionContext.getBar = function () {
19 | return bar;
20 | };
21 | },
22 | plugComponentContext: function plugComponentContext(componentContext) {
23 | componentContext.getFoo = function () {
24 | return foo;
25 | };
26 | },
27 | plugStoreContext: function plugStoreContext(storeContext) {
28 | storeContext.getFoo = function () {
29 | return foo;
30 | };
31 | },
32 | dehydrate: function () {
33 | return {
34 | bar: bar
35 | };
36 | },
37 | rehydrate: function (state, done) {
38 | return new Promise(function (resolve) {
39 | bar = state.bar;
40 | resolve();
41 | });
42 | }
43 | };
44 | },
45 | dehydrate: function dehydrate() {
46 | return {
47 | foo: foo
48 | };
49 | },
50 | rehydrate: function rehydrate(state) {
51 | return new Promise(function (resolve) {
52 | foo = state.foo;
53 | resolve();
54 | });
55 | }
56 | }
57 | };
58 |
--------------------------------------------------------------------------------
/packages/fluxible-addons-react/README.md:
--------------------------------------------------------------------------------
1 | # Fluxible Addons for React
2 |
3 | [](http://badge.fury.io/js/fluxible-addons-react)
4 |
10 |
11 | [Fluxible](https://github.com/yahoo/fluxible) addons for use with [React](https://github.com/facebook/react).
12 |
13 | ```bash
14 | $ npm install --save fluxible-addons-react
15 | ```
16 |
17 | Join the #fluxible channel of the [Reactiflux](http://reactiflux.com) Discord community.
18 |
19 | [](https://gitter.im/yahoo/fluxible)
20 |
21 | ## Docs
22 |
23 | * [connectToStores](https://github.com/yahoo/fluxible-addons-react/blob/master/docs/api/connectToStores.md)
24 | * [FluxibleComponent](https://github.com/yahoo/fluxible-addons-react/blob/master/docs/api/FluxibleComponent.md)
25 | * [FluxibleMixin](https://github.com/yahoo/fluxible-addons-react/blob/master/docs/api/FluxibleMixin.md) (legacy)
26 | * [provideContext](https://github.com/yahoo/fluxible-addons-react/blob/master/docs/api/provideContext.md)
27 | * [createElementWithContext](https://github.com/yahoo/fluxible-addons-react/blob/master/docs/api/createElementWithContext.md)
28 | * [batchedUpdatePlugin](https://github.com/yahoo/fluxible-addons-react/blob/master/docs/api/batchedUpdatePlugin.md)
29 |
30 | ## License
31 |
32 | This software is free to use under the Yahoo Inc. BSD license.
33 | See the [LICENSE file][] for license text and copyright information.
34 |
35 | [LICENSE file]: https://github.com/yahoo/fluxible/blob/master/LICENSE.md
36 |
--------------------------------------------------------------------------------
/packages/fluxible/docs/api/README.md:
--------------------------------------------------------------------------------
1 | # API Documentation
2 |
3 | `var Fluxible = require('fluxible')` exports the following:
4 |
5 | * [`Fluxible`](Fluxible.md)
6 |
7 |
8 | or if you're using ES6:
9 |
10 | ```js
11 | import Fluxible from 'fluxible';
12 | ```
13 |
14 | While building your application, you will need to build [stores](Stores.md), [actions](Actions.md) and connect them to your [components](Components.md).
15 |
16 | Occasionally you may find that you need to use [plugins](Plugins.md) to extend Fluxible's interfaces.
17 |
18 | ## Addons
19 |
20 | `var FluxibleAddons = require('fluxible/addons')` also provides helper utilities for creating stores:
21 |
22 | * [`FluxibleAddons.BaseStore`](addons/BaseStore.md)
23 | * [`FluxibleAddons.createStore`](addons/createStore.md)
24 |
25 | or if you're using ES6:
26 |
27 | ```js
28 | import {BaseStore, createStore} from 'fluxible/addons';
29 | ```
30 |
31 | These libraries are not bundled with the main Fluxible export because stores are decoupled from Fluxible.
32 |
33 | ## React Addons
34 |
35 | `npm i --save fluxible-addons-react`
36 |
37 | `var ReactAddons = require('fluxible-addons-react');` provides helpers for
38 | using Fluxible with React.
39 |
40 | * [`FluxibleAddons.connectToStores`](../../../../packages/fluxible-addons-react/docs/api/connectToStores.md)
41 | * [`FluxibleAddons.FluxibleComponent`](../../../../packages/fluxible-addons-react/docs/api/FluxibleComponent.md)
42 | * [`FluxibleAddons.FluxibleMixin`](../../../../packages/fluxible-addons-react/docs/api/FluxibleMixin.md)
43 | * [`FluxibleAddons.provideContext`](../../../../packages/fluxible-addons-react/docs/api/provideContext.md)
44 |
45 | or if you're using ES6:
46 |
47 | ```js
48 | import {connectToStores, FluxibleComponent, FluxibleMixin, provideContext} from 'fluxible-addons-react';
49 | ```
50 |
51 | These libraries are not bundled with Fluxible to allow for Fluxible usage with
52 | other view libraries such as React Native.
53 |
54 | ## Utils
55 |
56 | Fluxible also provides the following utilities for testing your components:
57 |
58 | * [createMockActionContext](Actions.md#testing)
59 | * [createMockComponentContext](Components.md#testing)
60 |
--------------------------------------------------------------------------------
/packages/fluxible-addons-react/docs/api/connectToStores.md:
--------------------------------------------------------------------------------
1 | # connectToStores
2 |
3 | ```js
4 | import connectToStores from 'fluxible-addons-react/connectToStores';
5 | ```
6 |
7 | `connectToStores` is a higher-order component that provides a convenient way to access state from the stores from within your component. It takes care of defining `getInitialState` and listening to the stores for updates. The store state will be sent to the `Component` instance as props. It is required that the React context is set and has access to `getStore`. It is recommended to use [`provideContext`](provideContext.md) around your top level component to do this for you.
8 |
9 | Takes the following parameters:
10 |
11 | * `Component` - the component that should receive the state as props, optional if using decorator pattern
12 | * `stores` - array of store constructors to listen for changes
13 | * `getStateFromStores` - function that receives all stores and should return the full state object. Receives `stores` hash and component `props` as arguments
14 | * `customContextTypes` (*optional*) - additional `contextTypes` that could be accessed from your `getStateFromStores` function
15 |
16 | ## Example
17 |
18 | The following example will listen to changes in `FooStore` and `BarStore` and pass `foo` and `bar` as props to the `Component` when it is instantiated.
19 |
20 | ```js
21 | class Component extends React.Component {
22 | render() {
23 | return (
24 |
25 |
{this.props.foo}
26 |
{this.props.bar}
27 |
28 | );
29 | }
30 | }
31 |
32 | Component = connectToStores(Component, [FooStore, BarStore], (context, props) => ({
33 | foo: context.getStore(FooStore).getFoo(),
34 | bar: context.getStore(BarStore).getBar()
35 | }));
36 |
37 | export default Component;
38 | ```
39 |
40 | ### Decorator
41 |
42 | You may also use the
43 | [decorator pattern](https://github.com/wycats/javascript-decorators):
44 |
45 | ```js
46 | @connectToStores([FooStore, BarStore], (context, props) => ({
47 | foo: context.getStore(FooStore).getFoo(),
48 | bar: context.getStore(BarStore).getBar()
49 | }))
50 | class Component extends React.Component {
51 | render() {
52 | return ;
53 | }
54 | }
55 | export default Component;
56 | ```
57 |
--------------------------------------------------------------------------------
/docs/community/libraries.md:
--------------------------------------------------------------------------------
1 | # Libraries
2 |
3 | Take a look at some of the libraries that our community has built. Edit this page on GitHub to list your libraries or plugins.
4 |
5 | ## General
6 |
7 | * [fumble](https://github.com/yahoo/fumble) - Simple error objects in node. Created specifically to be used with the [fetchr](https://github.com/yahoo/fetchr) library and based on [hapi.js](http://hapijs.com/)' [Boom](https://github.com/hapijs/boom).
8 |
9 | * [fluxible-plugin-service-proxy](https://github.com/benbria/node-fluxible-plugin-service-proxy) - Fluxible plugin for registering services with different implementations on the server and the client.
10 |
11 | * [fluxible-plugin-facebook](https://github.com/Hairfie/fluxible-plugin-facebook) - Fluxible plugin for integrating the facebook's official SDK in fluxible applications.
12 |
13 | * [fluxible-plugin-cookie](https://github.com/Hairfie/fluxible-plugin-cookie) - Fluxible plugin for writing and reading cookies.
14 |
15 | * [fluxible-plugin-middleware](https://github.com/geekyme/fluxible-plugin-middleware) - Fluxible plugin for adding middlewares in Redux style!
16 |
17 | ## Data Services
18 |
19 | * [fluxible-plugin-fetchr](https://github.com/yahoo/fluxible-plugin-fetchr) - Isomorphic data fetching services.
20 |
21 | ## Routing
22 |
23 | * [fluxible-router](https://github.com/yahoo/fluxible-router) - Flux-based routing using Fluxible.
24 | * [react-router](https://github.com/rackt/react-router) - React-centric routing.
25 |
26 | ## Actions
27 |
28 | * [fluxible-action-utils](https://github.com/yahoo/fluxible-action-utils) - utility methods to aid in writing [actions](http://fluxible.io/api/fluxible-context.html#executeaction-action-payload-callback-) for [fluxible](http://fluxible.io) based applications.
29 | * [fluxible-plugin-actions](https://github.com/gingur/fluxible-plugin-actions) - component context utility to get actions by key instead of path, useful for cross library components.
30 |
31 | ## Immutable.js
32 |
33 | * [fluxible-immutable-store](https://www.npmjs.com/packages/fluxible-immutable-store) - A helper for creating Fluxible/Dispatchr BaseStores that manage immutable data structures.
34 | * [fluxible-immutable-utils](https://github.com/yahoo/fluxible-immutable-utils) - A mixin that provides a convenient interface for using Immutable.js inside react components.
35 |
--------------------------------------------------------------------------------
/packages/fluxible/tests/unit/utils/createMockComponentContext.js:
--------------------------------------------------------------------------------
1 | /* jshint newcap:false */
2 | /* global describe, it, before, after */
3 |
4 | 'use strict';
5 |
6 | var expect = require('chai').expect;
7 | var mockery = require('mockery');
8 | var createMockComponentContext = require('../../../utils').createMockComponentContext;
9 |
10 | describe('createMockComponentContext', function () {
11 |
12 | describe('instance', function () {
13 | var context;
14 |
15 | before(function () {
16 | context = createMockComponentContext();
17 | });
18 |
19 | it('should have the following properties: dispatcher, executeActionCalls', function () {
20 | expect(context).to.have.property('executeActionCalls').that.is.an('array').and.empty;
21 | });
22 |
23 | describe('#getStore', function () {
24 | it('should delegate to the dispatcher getStore method', function () {
25 | expect(context).to.have.property('getStore');
26 | });
27 | });
28 |
29 | describe('#executeAction', function () {
30 | it('should provide an executeAction method', function () {
31 | expect(context).to.respondTo('executeAction');
32 | });
33 |
34 | it('should execute the action and pass a MockActionContext', function () {
35 | var mockPayload = {
36 | foo: 'bar',
37 | baz: 'fubar'
38 | };
39 |
40 | function mockAction (ctx, payload, done) {
41 | expect(ctx).to.be.an('object');
42 | expect(ctx.dispatch).to.be.a('function');
43 | expect(ctx.executeAction).to.be.a('function');
44 | expect(payload).to.equal(mockPayload);
45 | done();
46 | }
47 |
48 | context.executeAction(mockAction, mockPayload);
49 |
50 | expect(context.executeActionCalls).to.have.length(1);
51 |
52 | var call = context.executeActionCalls[0];
53 | expect(call).to.have.property('action', mockAction);
54 | expect(call).to.have.property('payload', mockPayload);
55 | });
56 | });
57 | });
58 |
59 | after(function() {
60 | mockery.deregisterAll();
61 | mockery.disable();
62 | });
63 | });
64 |
--------------------------------------------------------------------------------
/packages/fluxible-addons-react/docs/api/provideContext.md:
--------------------------------------------------------------------------------
1 | # provideContext
2 |
3 | ```js
4 | import provideContext from 'fluxible-addons-react/provideContext';
5 | ```
6 |
7 | `provideContext` wraps the `Component` with a higher-order component that
8 | specifies the child context for you. This allows the React context to propagate
9 | to all children that specify their `contextTypes`.
10 |
11 | Receives the following parameters:
12 |
13 | * `Component`: the component to wrap, typically your top-level application
14 | component, optional if using decorator pattern
15 | * `customContextTypes`: additional `childContextTypes` to add; useful for
16 | plugins that add to the component context
17 |
18 | By default, the `executeAction` and `getStore` methods will be added to the
19 | child context and `customContextTypes` will be merged with these defaults.
20 |
21 | ## Example
22 |
23 | The most typical and basic usage of `provideContext` is to wrap your
24 | Application component to ensure that it receives the `getStore` and
25 | `executeAction` methods.
26 |
27 | ```js
28 | class Application extends React.Component {
29 | render() {
30 | ...
31 | }
32 | }
33 | Application = provideContext(Application);
34 | export default Application;
35 | ```
36 |
37 | Now when you render the Application component, you can pass in the component
38 | context via the `context` prop and be assured that all children will have
39 | access to it by specifying the respective `contextType`.
40 |
41 | ```js
42 | ReactDOM.renderToString();
43 | ```
44 |
45 | If you're using the
46 | [`createElementWithContext()` method](createElementWithContext.md) and you
47 | passed a higher-order `provideContext` component to the Fluxible constructor,
48 | then the `context` prop will automatically be passed in for you.
49 |
50 | ### Plugins and Custom Component Context
51 |
52 | Since plugins have the ability to add new methods or properties to the
53 | component context, you can specify custom `childContextTypes` through the
54 | second parameter.
55 |
56 | ```js
57 | Application = provideContext(Application, {
58 | foo: React.PropTypes.string
59 | });
60 | ```
61 |
62 | ### Decorator
63 |
64 | You may also use the
65 | [decorator pattern](https://github.com/wycats/javascript-decorators):
66 |
67 | ```js
68 | @provideContext({
69 | foo: React.PropTypes.string
70 | })
71 | class Application extends React.Component {
72 | render() {
73 | ...
74 | }
75 | }
76 | export default Application;
77 | ```
78 |
--------------------------------------------------------------------------------
/packages/fluxible-reducer-store/tests/fixtures/stores/MessageStore.js:
--------------------------------------------------------------------------------
1 | import {createReducerStore} from '../../../index';
2 |
3 | const reducers = {
4 | RECEIVE_MESSAGES: (state, messages) => {
5 | var oldMessages = state.messages;
6 | var newMessages = {...oldMessages};
7 | messages.forEach(function (message) {
8 | newMessages[message.id] = {
9 | ...message,
10 | isRead: false
11 | };
12 | });
13 | var sortedByDate = (newMessages && Object.keys(newMessages)) || [];
14 | sortedByDate.sort(function (a, b) {
15 | if (newMessages[a].date < newMessages[b].date) {
16 | return -1;
17 | } else if (newMessages[a].date > newMessages[b].date) {
18 | return 1;
19 | }
20 | return 0;
21 | });
22 |
23 | return {
24 | messages: newMessages,
25 | sortedByDate
26 | };
27 | },
28 | OPEN_THREAD: (state, payload) => {
29 | // Mark all read
30 | var oldMessages = state.messages;
31 | var newMessages = {
32 | ...oldMessages
33 | };
34 | Object.keys(state.messages).forEach((key) => {
35 | var message = state.messages[key];
36 | if (message.threadID === payload.threadID) {
37 | newMessages[key] = {
38 | ...message,
39 | text: message.text + 'foo',
40 | isRead: true
41 | };
42 | }
43 | });
44 | return {
45 | ...state,
46 | messages: newMessages
47 | };
48 | }
49 | };
50 |
51 | var getters = {
52 | getAll: function getAll(state) {
53 | return state.messages;
54 | },
55 | get: function get(state, id) {
56 | return state.messages[id];
57 | },
58 | getAllForThread: function getAllForThread(state, threadID) {
59 | var threadMessages = [];
60 | state.sortedByDate.forEach(function (key) {
61 | var message = state.messages[key];
62 | if (message.threadID === threadID) {
63 | threadMessages.push(message);
64 | }
65 | });
66 | return threadMessages;
67 | }
68 | };
69 |
70 | var MessageStore = createReducerStore({
71 | storeName: 'MessageStore',
72 | initialState: {
73 | messages: {},
74 | sortedByDate: []
75 | },
76 | reducers: reducers,
77 | getters: getters
78 | });
79 |
80 | export default MessageStore;
81 |
--------------------------------------------------------------------------------
/packages/generator-fluxible/app/templates/server.js:
--------------------------------------------------------------------------------
1 | /**
2 | * This leverages Express to create and run the http server.
3 | * A Fluxible context is created and executes the navigateAction
4 | * based on the URL. Once completed, the store state is dehydrated
5 | * and the application is rendered via React.
6 | */
7 |
8 | import express from 'express';
9 | import compression from 'compression';
10 | import bodyParser from 'body-parser';
11 | import path from 'path';
12 | import serialize from 'serialize-javascript';
13 | import {navigateAction} from 'fluxible-router';
14 | import debugLib from 'debug';
15 | import React from 'react';
16 | import ReactDOM from 'react-dom/server';
17 | import app from './app';
18 | import HtmlComponent from './components/Html';
19 | import { createElementWithContext } from 'fluxible-addons-react';
20 | const env = process.env.NODE_ENV;
21 |
22 | const debug = debugLib('<%= name %>');
23 |
24 | const server = express();
25 | server.use('/public', express.static(path.join(__dirname, '/build')));
26 | server.use(compression());
27 | server.use(bodyParser.json());
28 |
29 | server.use((req, res, next) => {
30 | const context = app.createContext();
31 |
32 | debug('Executing navigate action');
33 | context.getActionContext().executeAction(navigateAction, {
34 | url: req.url
35 | }, (err) => {
36 | if (err) {
37 | if (err.statusCode && err.statusCode === 404) {
38 | // Pass through to next middleware
39 | next();
40 | } else {
41 | next(err);
42 | }
43 | return;
44 | }
45 |
46 | debug('Exposing context state');
47 | const exposed = 'window.App=' + serialize(app.dehydrate(context)) + ';';
48 |
49 | debug('Rendering Application component into html');
50 | const markup = ReactDOM.renderToString(createElementWithContext(context));
51 | const htmlElement = React.createElement(HtmlComponent, {
52 | clientFile: env === 'production' ? 'main.min.js' : 'main.js',
53 | context: context.getComponentContext(),
54 | state: exposed,
55 | markup: markup
56 | });
57 | const html = ReactDOM.renderToStaticMarkup(htmlElement);
58 |
59 | debug('Sending markup');
60 | res.type('html');
61 | res.write('' + html);
62 | res.end();
63 | });
64 | });
65 |
66 | const port = process.env.PORT || 3000;
67 | server.listen(port);
68 | console.log('Application listening on port ' + port);
69 |
70 | export default server;
71 |
--------------------------------------------------------------------------------
/packages/fluxible-reducer-store/tests/unit/lib/createReducerStore.js:
--------------------------------------------------------------------------------
1 | /*global describe,it,beforeEach */
2 |
3 | import {expect} from 'chai';
4 | import MessageStore from '../../fixtures/stores/MessageStore';
5 | import SingleReducerStore from '../../fixtures/stores/SingleReducerStore';
6 | import messages from '../../fixtures/data/messages';
7 | import {createMockActionContext} from 'fluxible/utils';
8 |
9 | describe('fluxible-reducer-store', () => {
10 | var context;
11 | beforeEach(() => {
12 | context = createMockActionContext({
13 | stores: [MessageStore, SingleReducerStore]
14 | });
15 | });
16 | it('should receive messages', () => {
17 | const messageStore = context.getStore(MessageStore);
18 | const singleStore = context.getStore(SingleReducerStore);
19 | expect(messageStore).to.be.instanceof(MessageStore);
20 | context.dispatch('RECEIVE_MESSAGES', messages);
21 | const threadMessages = messageStore.getAllForThread('t_1');
22 | expect(singleStore.getState().count).to.equal(7);
23 | expect(threadMessages).to.be.an('array');
24 | expect(threadMessages.length).to.equal(3);
25 | });
26 | it('should handle open thread', () => {
27 | const store = context.getStore(MessageStore);
28 | const singleStore = context.getStore(SingleReducerStore);
29 | context.dispatch('RECEIVE_MESSAGES', messages);
30 | const threadMessages = store.getAllForThread('t_1');
31 | threadMessages.forEach((message) => {
32 | expect(message.isRead).to.equal(false);
33 | });
34 | context.dispatch('OPEN_THREAD', {
35 | threadID: 't_1'
36 | });
37 | expect(singleStore.getState().count).to.equal(7);
38 | const newThreadMessages = store.getAllForThread('t_1');
39 | newThreadMessages.forEach((message) => {
40 | expect(message.isRead).to.equal(true);
41 | });
42 | });
43 | it('should dehydrate correctly', () => {
44 | const store = context.getStore(MessageStore);
45 | context.dispatch('RECEIVE_MESSAGES', messages);
46 | const data = store.dehydrate();
47 | expect(data).to.deep.equal(store.getState());
48 | });
49 | it('should rehydrate correctly', () => {
50 | const store = context.getStore(MessageStore);
51 | context.dispatch('RECEIVE_MESSAGES', messages);
52 | const data = store.dehydrate();
53 | const beforeThreadMessages = store.getAllForThread('t_1');
54 | const newStore = new MessageStore();
55 | newStore.rehydrate(data);
56 | const afterThreadMessages = newStore.getAllForThread('t_1');
57 | expect(afterThreadMessages).to.deep.equal(beforeThreadMessages);
58 | });
59 | });
60 |
--------------------------------------------------------------------------------
/docs/faq.md:
--------------------------------------------------------------------------------
1 | # FAQ
2 |
3 | Frequently asked questions from the community.
4 |
5 |
6 | ## General
7 |
8 | ### What are the drawbacks of using Fluxible? In contrast to Facebook's Flux library?
9 |
10 | It depends on whether you are building a server-side app. Facebook’s Flux only works on the client, but it may be easier to figure out since it doesn’t have to deal with concurrency issues.
11 |
12 | ### Is there a way to render a reactComponent only client side?
13 |
14 | Use the `componentDidMount` lifecycle event to set an internal state that re-renders itself on the client (i.e, `this.state.mounted`).
15 |
16 |
17 |
18 |
19 | ## Actions
20 |
21 | ### Can I fire the navigation function manually?
22 |
23 | Yes, you can call the [`navigate` action](https://github.com/yahoo/flux-router-component/blob/master/actions/navigate.js) from flux-router-component directly.
24 |
25 |
26 | ## Components
27 |
28 | ### Should `getStore` and `executeAction` be required in the `contextTypes` of `FluxibleMixin`?
29 |
30 | It is not required because we still support passing `context` as a prop to all of the components that need it. This is how things were done before we started using `context`. Some people still prefer this method since React’s `context` is not guaranteed to stay the way it is right now.
31 |
32 |
33 | ## Stores
34 |
35 | ### Can I call an action from a store?
36 |
37 | Right now, it is not possible. This will require some thought on how to ensure that the server can keep track of actions that are executed from stores.
38 |
39 |
40 |
41 |
42 | ## Data Fetching
43 |
44 | ### What does the error message "invalid csrf token" from Fetchr mean?
45 |
46 | This is caused by the csrf middleware. You need to make sure you pass the `csrf` token to the `createContext` method on the server. Check the server.js of [chat](https://github.com/yahoo/flux-examples/blob/master/chat/server.js#L37) or [todo](https://github.com/yahoo/flux-examples/blob/master/todo/server.js#L41) examples.
47 |
48 |
49 |
50 |
51 | ## Routing
52 |
53 | ### Which should I use, `react-router` or `flux-router-component`?
54 |
55 | [react-router](https://github.com/rackt/react-router) has some features that flux-router-component does not have, like nested routes, component `willTransitionTo` hooks, and built-in re-directs. If you feel like you do not need them, then you will be fine with flux-router-component. We prefer the flux-like flow, so we use flux-router-component internally.
56 |
57 |
58 | ### In react-router, how do I get access to the Fluxible context from `willTransitionTo`?
59 |
60 | Since `willTransitionTo` is defined statically on the component, it will not have access to the Fluxible context. There is a [open issue](https://github.com/rackt/react-router/pull/590) from react-router to provide access but, as of this writing) it has yet to be resolved.
61 |
--------------------------------------------------------------------------------
/packages/fluxible-addons-react/provideContext.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2015, Yahoo Inc.
3 | * Copyrights licensed under the New BSD License. See the accompanying LICENSE file for terms.
4 | */
5 | 'use strict';
6 |
7 | var React = require('react');
8 | var hoistNonReactStatics = require('hoist-non-react-statics');
9 |
10 | function createComponent(Component, customContextTypes) {
11 | var componentName = Component.displayName || Component.name;
12 | var childContextTypes = Object.assign({
13 | executeAction: React.PropTypes.func.isRequired,
14 | getStore: React.PropTypes.func.isRequired
15 | }, customContextTypes || {});
16 |
17 | var ContextProvider = React.createClass({
18 | displayName: componentName + 'ContextProvider',
19 |
20 | propTypes: {
21 | context: React.PropTypes.object.isRequired
22 | },
23 |
24 | childContextTypes: childContextTypes,
25 |
26 | getChildContext: function () {
27 | var childContext = {
28 | executeAction: this.props.context.executeAction,
29 | getStore: this.props.context.getStore
30 | };
31 | if (customContextTypes) {
32 | Object.keys(customContextTypes).forEach(function (key) {
33 | childContext[key] = this.props.context[key];
34 | }, this);
35 | }
36 | return childContext;
37 | },
38 |
39 | render: function () {
40 | return React.createElement(Component, Object.assign({}, this.props, { ref: 'wrappedElement' }));
41 | }
42 | });
43 |
44 | hoistNonReactStatics(ContextProvider, Component);
45 |
46 | return ContextProvider;
47 | }
48 |
49 | /**
50 | * Provides context prop to all children as React context
51 | *
52 | * Example:
53 | * var WrappedComponent = provideContext(Component, {
54 | * foo: React.PropTypes.string
55 | * });
56 | *
57 | * Also supports the decorator pattern:
58 | * @provideContext({
59 | * foo: React.PropTypes.string
60 | * })
61 | * class ConnectedComponent extends React.Component {
62 | * render() {
63 | * return ;
64 | * }
65 | * }
66 | *
67 | * @method provideContext
68 | * @param {React.Component} [Component] component to wrap
69 | * @param {object} customContextTypes Custom contextTypes to add
70 | * @returns {React.Component} or {Function} if using decorator pattern
71 | */
72 | module.exports = function provideContext(Component, customContextTypes) {
73 | // support decorator pattern
74 | if (arguments.length === 0 || typeof arguments[0] !== 'function') {
75 | customContextTypes = arguments[0];
76 | return function connectToStoresDecorator(ComponentToDecorate) {
77 | return createComponent(ComponentToDecorate, customContextTypes);
78 | };
79 | }
80 |
81 | return createComponent.apply(null, arguments);
82 | };
83 |
--------------------------------------------------------------------------------
/packages/generator-fluxible/app/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2015, Yahoo! Inc.
3 | * Copyrights licensed under the New BSD License. See the accompanying LICENSE file for terms.
4 | */
5 |
6 | 'use strict';
7 | var yeoman = require('yeoman-generator');
8 | var chalk = require('chalk');
9 | var yosay = require('yosay');
10 | var str = require('underscore.string');
11 |
12 | module.exports = yeoman.generators.Base.extend({
13 | initializing: function () {
14 | this.pkg = require('../package.json');
15 | },
16 |
17 | prompting: function () {
18 | var done = this.async();
19 |
20 | this.log(yosay(
21 | 'Welcome to the riveting ' + chalk.red('Fluxible') + ' generator!'
22 | ));
23 |
24 | var prompts = [{
25 | type: 'input',
26 | name: 'name',
27 | default: this.appname,
28 | message: 'Project name:',
29 | validate: function(input) {
30 | return !!input;
31 | }
32 | }];
33 |
34 | this.prompt(prompts, function (props) {
35 | this.displayName = props.name;
36 | this.name = str.slugify(props.name);
37 | this.buildSystem = str.slugify(props.buildSystem);
38 | done();
39 | }.bind(this));
40 | },
41 |
42 | writing: {
43 | config: function () {
44 | this.template('.babelrc', '.babelrc', this.context);
45 | this.template('.editorconfig', '.editorconfig', this.context);
46 | // .gitignore is renamed by npm to .npmignore, so use underscore
47 | this.template('_gitignore', '.gitignore', this.context);
48 | this.template('.eslintrc', '.eslintrc', this.context);
49 | this.template('package.json', 'package.json', this.context);
50 | },
51 |
52 | projectfiles: function () {
53 | this.template('app.js', 'app.js', this.context);
54 | this.template('client.js', 'client.js', this.context);
55 | this.template('server.js', 'server.js', this.context);
56 | this.template('start.js', 'start.js', this.context);
57 | this.template('webpack.config.js', 'webpack.config.js', this.context);
58 | this.template('webpack.config.production.js', 'webpack.config.production.js', this.context);
59 | this.template('webpack-dev-server.js', 'webpack-dev-server.js', this.context);
60 | this.directory('actions', 'actions', this.context);
61 | this.directory('components', 'components', this.context);
62 | this.directory('configs', 'configs', this.context);
63 | this.directory('stores', 'stores', this.context);
64 | // Webpack dev server needs this folder to exist before starting
65 | this.template('_buildgitignore', 'build/js/.gitignore', this.context);
66 | }
67 | },
68 |
69 | install: function () {
70 | this.installDependencies({
71 | npm: true,
72 | bower: false,
73 | skipInstall: this.options['skip-install']
74 | });
75 | }
76 | });
77 |
--------------------------------------------------------------------------------
/packages/fluxible-addons-react/tests/unit/lib/FluxibleComponent.js:
--------------------------------------------------------------------------------
1 | /*globals describe,it,afterEach,beforeEach*/
2 | 'use strict';
3 |
4 | var expect = require('chai').expect;
5 | var mockery = require('mockery');
6 | var React;
7 | var ReactDOM;
8 | var ReactTestUtils;
9 | var connectToStores;
10 | var provideContext;
11 | var FluxibleComponent;
12 | var FooStore = require('../../fixtures/stores/FooStore');
13 | var BarStore = require('../../fixtures/stores/BarStore');
14 | var createMockComponentContext = require('fluxible/utils/createMockComponentContext');
15 | var jsdom = require('jsdom');
16 |
17 | describe('fluxible-addons-react', function () {
18 | describe('FluxibleComponent', function () {
19 | var context;
20 |
21 | beforeEach(function (done) {
22 | jsdom.env('', [], function (err, window) {
23 | if (err) {
24 | done(err);
25 | }
26 | global.window = window;
27 | global.document = window.document;
28 | global.navigator = window.navigator;
29 |
30 | context = createMockComponentContext({
31 | stores: [FooStore, BarStore]
32 | });
33 |
34 | mockery.enable({
35 | warnOnReplace: false,
36 | warnOnUnregistered: false,
37 | useCleanCache: true
38 | });
39 | React = require('react');
40 | ReactDOM = require('react-dom');
41 | ReactTestUtils = require('react-addons-test-utils');
42 | connectToStores = require('../../../').connectToStores;
43 | provideContext = require('../../../').provideContext;
44 | FluxibleComponent = require('../../../').FluxibleComponent;
45 |
46 | done();
47 | });
48 | });
49 |
50 | afterEach(function () {
51 | delete global.window;
52 | delete global.document;
53 | delete global.navigator;
54 | mockery.disable();
55 | });
56 |
57 | it('will not double render', function () {
58 | const Component = React.createClass({
59 | render: function () {
60 | return (
61 |
82 | );
83 | }
84 | });
85 | const element = ReactTestUtils.renderIntoDocument(
86 |
87 | Some child
88 |
89 | );
90 | });
91 | });
92 | });
93 |
--------------------------------------------------------------------------------
/docs/community/reference-applications.md:
--------------------------------------------------------------------------------
1 | # Reference Applications
2 |
3 | Want to add your application? Click edit above and open a PR!
4 |
5 | ## Open Source
6 |
7 | - [Contactor](https://github.com/localnerve/flux-react-example) ([live link](http://flux-react-example.herokuapp.com)) is a data-driven Fluxible demo application. Uses [Fetchr](https://github.com/yahoo/fetchr), AMQP, and Github as a simple data service. Another possible useful source of inspiration for solving problems encountered while developing an isomorphic application.
8 | - [Dockunit.io](https://github.com/tlovett1/dockunit-platform/) ([live link](http://dockunit.io)) is a containerized continuous integration testing service for running [Dockunit](https://github.com/tlovett1/dockunit) test suites hosted on Github. Built on Fluxible and Docker. It is 100% open source.
9 | - [Flux Examples](https://github.com/yahoo/flux-examples) contains various Flux examples built with Fluxible.
10 | - [Fluxible.io](https://github.com/yahoo/fluxible.io) is this website! Showcases data fetching, routing, analytics, search and more.
11 | - [isomorphic500](https://github.com/gpbl/isomorphic500) ([live link](https://isomorphic500.herokuapp.com/)) is a Fluxible demo application showing photos from 500px. It uses the official Fluxible plugins, supports multiple language and includes a full-stack development environment. Useful as a source of inspiration to solve some common cases encountered while developing an isomorphic application.
12 | - [quad-blog](https://github.com/cesarandreu/quad-blog/) ([live link](https://blog.cesarandreu.com/)) is a blog written with Fluxible. Another possible useful source of inspiration for solving problems encountered while developing an isomorphic application.
13 |
14 | ## Closed Source
15 |
16 | - [6play.fr (Beta)](http://beta.6play.fr) is the video site for the french television Channel "M6"
17 | - [CodePad](https://codepad.remoteinterview.io) is an All-in-one pair programming tool ideal for phone interviews. Supports and compiles 10+ languages and has full bash access too.
18 | - [DICE FM](https://dice.fm) is a music startup based in London that handpicks the best gigs in your city.
19 | - [Docker Hub (Beta)](https://hub-beta.docker.com/) is a cloud-based platform service for distributed applications. It includes container image distribution and change management, user and team collaboration, and lifecycle workflow automation.
20 | - [LD-R](http://ld-r.org) Linked Data-driven Web Components.
21 | - [Mailjet Passport](https://www.mailjet.com/passport) is an email builder app (WYSIWYG & HTML Editor), automagically generating responsive emails.
22 | - [Porch](https://porch.com/) is the home network, helping homeowners make informed decisions on home improvements and in selecting the right home professionals.
23 | - [ShaderFrog](http://shaderfrog.com) is a WebGL, Three.js, Fluxible browser-based shader editor that can export GLSL shaders to multiple targets.
24 | - [SVT Sport](http://www.svt.se/sport/) is a isomorphic site providing sports coverage for Swedish Television.
25 | - [Yahoo Beauty](https://www.yahoo.com/beauty) is Yahoo's first digital magazine on Fluxible.
26 | - International Magazines:
27 | - [https://uk.style.yahoo.com/](https://uk.style.yahoo.com/)
28 | - [https://fr.people.yahoo.com/](https://fr.people.yahoo.com/)
29 | - [Yahoo Daily Fantasy](https://sports.yahoo.com/dailyfantasy) is a fantasy sports app where users can compete everyday with one-day or weeklong contests, setting lineups with simplified salary caps and easy access to news and scores from Yahoo’s Fantasy experts.
30 | - [Yahoo NFL Stream](https://nflstream.yahoo.com/) is the live video streaming site for the NFL on Yahoo.
31 | - [Yahoo Search SDK](https://developer.yahoo.com/search-sdk/apps/) is a software developer toolkit to integrate Yahoo Search into native apps.
32 | - [Yahoo Taiwan](https://tw.mobi.yahoo.com/) is the mobile frontpage for Yahoo Taiwan.
33 |
--------------------------------------------------------------------------------
/gulpfile.babel.js:
--------------------------------------------------------------------------------
1 | import fs from 'fs';
2 | import path from 'path';
3 | import gulp from 'gulp';
4 | import {cd, exec, rm} from 'shelljs';
5 | import {argv} from 'yargs';
6 |
7 | const ROOT_PATH = path.resolve(__dirname);
8 | const PACKAGES_PATH = path.resolve(__dirname, './packages');
9 | const packages = fs.readdirSync(PACKAGES_PATH).filter((file) => {
10 | return fs.statSync(path.resolve(PACKAGES_PATH, file)).isDirectory();
11 | }).reduce((acc, file) => {
12 | return {
13 | ...acc,
14 | [file]: path.resolve(PACKAGES_PATH, file)
15 | };
16 | }, {});
17 |
18 | const sharedDeps = [
19 | 'react',
20 | 'react-dom'
21 | ];
22 | gulp.task('install', () => {
23 | return Promise.all(
24 | // Link all packages to the root
25 | Object.keys(packages).map((packageName) => {
26 | return new Promise((resolve) => {
27 | cd(packages[packageName]);
28 | exec('npm link');
29 | cd(ROOT_PATH);
30 | exec('npm link ' + packageName);
31 | resolve();
32 | });
33 | })
34 | ).then(() => {
35 | // Remove duplicated packages and shared dependencies so they are loaded
36 | // from the top
37 | return Promise.all(
38 | Object.keys(packages).map((packageName) => {
39 | return Promise.all(
40 | Object.keys(packages).concat(sharedDeps).map((dependencyName) => {
41 | return new Promise((resolve) => {
42 | rm('-rf', path.resolve(packages[packageName], 'node_modules', dependencyName));
43 | resolve();
44 | });
45 | })
46 | );
47 | })
48 | );
49 | });
50 | });
51 |
52 | gulp.task('clean', () => {
53 | // Remove package node_modules
54 | return Promise.all(
55 | Object.keys(packages).map((packageName) => {
56 | return new Promise((resolve) => {
57 | rm('-rf', path.resolve(packages[packageName], 'node_modules'));
58 | resolve();
59 | });
60 | })
61 | );
62 | });
63 |
64 | gulp.task('version', () => {
65 | // Try to derive package name from directory where this was run from
66 | let pwd = process.env.PWD;
67 | let pwdPackageName = Object.keys(packages).reduce((prev, name) => {
68 | return packages[name] === pwd ? name : prev;
69 | }, undefined);
70 |
71 | // Check params
72 | let packageName = argv.pkg || argv.p || pwdPackageName;
73 | let version = argv.version || argv.v;
74 | if (!packageName || !version) {
75 | throw new Error('Usage: gulp version -p -v ');
76 | }
77 |
78 | // Bump the version
79 | cd(packages[packageName]);
80 | let execResult = exec('npm version ' + version);
81 | let bumpedVersion = execResult.output.replace('\n', '').replace('v', '');
82 |
83 | // Commit and tag
84 | exec('git add ' + packages[packageName] + '/package.json');
85 | let message = packageName + '@' + bumpedVersion;
86 | exec('git commit -m "' + message + '"');
87 | let tagName = packageName + '-v' + bumpedVersion;
88 | exec('git tag ' + tagName);
89 | });
90 |
91 | gulp.task('publish', () => {
92 | // Try to derive package name from directory where this was run from
93 | let pwd = process.env.PWD;
94 | let pwdPackageName = Object.keys(packages).reduce((prev, name) => {
95 | return packages[name] === pwd ? name : prev;
96 | }, undefined);
97 |
98 | // Check params
99 | let packageName = argv.pkg || argv.p || pwdPackageName;
100 | if (!packageName) {
101 | throw new Error('Usage: gulp publish -p ');
102 | }
103 |
104 | // Publish
105 | cd(packages[packageName]);
106 | exec('npm publish');
107 | });
108 |
109 |
--------------------------------------------------------------------------------
/packages/fluxible/tests/unit/utils/createMockActionContext.js:
--------------------------------------------------------------------------------
1 | /* jshint newcap:false */
2 | /* global describe, it, beforeEach, after */
3 |
4 | 'use strict';
5 |
6 | var expect = require('chai').expect;
7 | var mockery = require('mockery');
8 | var createMockActionContext = require('../../../utils').createMockActionContext;
9 |
10 | describe('createMockActionContext', function () {
11 |
12 | describe('instance', function () {
13 | var context;
14 |
15 | beforeEach(function () {
16 | context = createMockActionContext();
17 | });
18 |
19 | it('should have executeActionCalls property', function () {
20 | expect(context).to.have.property('executeActionCalls').that.is.an('array').and.empty;
21 | });
22 |
23 | it('should have dispatchCalls property', function () {
24 | expect(context).to.have.property('dispatchCalls').that.is.an('array').and.empty;
25 | });
26 |
27 | it('should have dispatcherContext property', function () {
28 | expect(context).to.have.property('dispatcherContext').that.is.an('object');
29 | });
30 |
31 | describe('#getStore', function () {
32 | it('should delegate to the dispatcher getStore method', function () {
33 | expect(context).to.have.property('getStore');
34 | });
35 | });
36 |
37 | describe('#executeAction', function () {
38 | var mockPayload = {
39 | foo: 'bar',
40 | baz: 'fubar'
41 | };
42 |
43 | function mockAction (ctx, payload, cb) {
44 | expect(ctx).to.be.an('object');
45 | expect(ctx.dispatch).to.be.a('function');
46 | expect(ctx.executeAction).to.be.a('function');
47 | expect(payload).to.equal(mockPayload);
48 | return cb();
49 | }
50 |
51 | function cbResult (done) {
52 | return function cb () {
53 | expect(context.executeActionCalls).to.have.length(1);
54 |
55 | var call = context.executeActionCalls[0];
56 | expect(call).to.have.property('action', mockAction);
57 | expect(call).to.have.property('payload', mockPayload);
58 | return done();
59 | }
60 | }
61 |
62 | it('should provide an executeAction method', function () {
63 | expect(context).to.respondTo('executeAction');
64 | });
65 |
66 | it('should execute the action and infer to return a Promise, success', function (done) {
67 | context.executeAction(mockAction, mockPayload).then(cbResult(done));
68 | });
69 |
70 | it('should execute the action and infer to return a Promise, failure', function (done) {
71 | function mockActionFailure (ctx, payload, cb) {
72 | cb(new Error('mock'));
73 | }
74 |
75 | context.executeAction(mockActionFailure, mockPayload).then(function() {
76 | done(new Error('should not have resolved successfully'));
77 | }).catch(function (error) {
78 | expect(error).to.be.an('Error');
79 | done();
80 | });
81 | });
82 |
83 | it('should execute the action and infer to NOT return a Promise', function () {
84 | var returnValue = 'mock';
85 | var result = context.executeAction(mockAction, mockPayload, cbResult(function () {
86 | return returnValue;
87 | }));
88 | expect(result).to.equal(returnValue);
89 | });
90 | });
91 |
92 | describe.skip('#dispatch', function () {
93 | });
94 | });
95 |
96 | after(function() {
97 | mockery.deregisterAll();
98 | mockery.disable();
99 | });
100 | });
101 |
--------------------------------------------------------------------------------
/packages/fluxible-reducer-store/lib/createReducerStore.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2015, Yahoo! Inc.
3 | * Copyrights licensed under the New BSD License. See the accompanying LICENSE file for terms.
4 | */
5 | 'use strict';
6 | var createStore = require('fluxible/addons').createStore;
7 | var __DEV__ = process.env.NODE_ENV !== 'production';
8 |
9 | function stateIdentity(state) { return state; }
10 |
11 | module.exports = function createReducerStore(spec) {
12 | spec = spec || {};
13 | var ReducerStore = createStore({
14 | storeName: spec.storeName,
15 | handlers: {},
16 | initialize: function () {
17 | this.state = spec.initialState;
18 | },
19 | getState: function () {
20 | return this.state;
21 | },
22 | dehydrate: function () {
23 | return (spec.dehydrate || stateIdentity)(this.state);
24 | },
25 | rehydrate: function (state) {
26 | this.state = (spec.rehydrate || stateIdentity)(state);
27 | }
28 | });
29 |
30 | function replaceReducers(reducers) {
31 | spec.reducers = reducers || {};
32 | // Allow a single reducer function
33 | if (spec.reducer) {
34 | spec.reducers = {
35 | 'default': spec.reducer
36 | };
37 | }
38 | // Create a handler proxy for each reducer so that the store can
39 | // still be lazy instantiated by dispatchr.
40 | Object.keys(spec.reducers).forEach(function eachReducer(eventName) {
41 | var reducer = spec.reducers[eventName];
42 | if (!ReducerStore.handlers[eventName]) {
43 | ReducerStore.handlers[eventName] = function (payload, dispatchedEventName) {
44 | if (__DEV__) {
45 | if (!spec.reducers[eventName]) {
46 | // Calling a reducer that has been removed should be
47 | // non-fatal, but warning may help with bugs.
48 | console.warn(eventName + ' reducer on ' + spec.storeName +
49 | ' has been removed.');
50 | return;
51 | }
52 | }
53 | var startingState = this.state;
54 | this.state = spec.reducers[eventName](this.state, payload, dispatchedEventName);
55 | if (this.state !== startingState) {
56 | this.emitChange();
57 | }
58 | };
59 | }
60 | });
61 | }
62 | ReducerStore.replaceReducer = replaceReducers;
63 | ReducerStore.replaceReducers = replaceReducers;
64 |
65 | ReducerStore.replaceGetters = function replaceGetters(getters) {
66 | spec.getters = getters || {};
67 | Object.keys(spec.getters).forEach(function (methodName) {
68 | if (!ReducerStore.prototype[methodName]) {
69 | ReducerStore.prototype[methodName] = function () {
70 | if (__DEV__) {
71 | if (!spec.getters[methodName]) {
72 | // Calling a method that has been removed would be
73 | // fatal, so let's throw a more useful error than
74 | // "undefined is not a function"
75 | throw new Error(methodName + ' on ' + storeName +
76 | ' has been removed but was still called.');
77 | }
78 | }
79 | var state = this.state;
80 | var args = [state].concat(Array.prototype.slice.call(arguments));
81 | return spec.getters[methodName].apply(null, args);
82 | };
83 | }
84 | });
85 | };
86 |
87 | ReducerStore.replaceSpec = function replaceSpec(spec) {
88 | spec = spec || {};
89 | ReducerStore.replaceReducers(spec.reducers);
90 | ReducerStore.replaceGetters(spec.getters);
91 | };
92 |
93 | ReducerStore.replaceSpec(spec);
94 |
95 | return ReducerStore;
96 | };
97 |
--------------------------------------------------------------------------------
/packages/fluxible-addons-react/connectToStores.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2015, Yahoo Inc.
3 | * Copyrights licensed under the New BSD License. See the accompanying LICENSE file for terms.
4 | */
5 | 'use strict';
6 |
7 | var React = require('react');
8 | var hoistNonReactStatics = require('hoist-non-react-statics');
9 |
10 | function createComponent(Component, stores, getStateFromStores, customContextTypes) {
11 | var componentName = Component.displayName || Component.name;
12 | var componentContextTypes = Object.assign({
13 | getStore: React.PropTypes.func.isRequired
14 | }, customContextTypes);
15 | var StoreConnector = React.createClass({
16 | displayName: componentName + 'StoreConnector',
17 | contextTypes: componentContextTypes,
18 | getInitialState: function getInitialState() {
19 | return this.getStateFromStores();
20 | },
21 | componentDidMount: function componentDidMount() {
22 | stores.forEach(function storesEach(Store) {
23 | this.context.getStore(Store).addChangeListener(this._onStoreChange);
24 | }, this);
25 | },
26 | componentWillUnmount: function componentWillUnmount() {
27 | stores.forEach(function storesEach(Store) {
28 | this.context.getStore(Store).removeChangeListener(this._onStoreChange);
29 | }, this);
30 | },
31 | componentWillReceiveProps: function componentWillReceiveProps(nextProps){
32 | this.setState(this.getStateFromStores(nextProps));
33 | },
34 | getStateFromStores: function (props) {
35 | props = props || this.props;
36 | return getStateFromStores(this.context, props);
37 | },
38 | _onStoreChange: function onStoreChange() {
39 | if (this.isMounted()) {
40 | this.setState(this.getStateFromStores());
41 | }
42 | },
43 | render: function render() {
44 | return React.createElement(Component, Object.assign({}, this.props, this.state, {ref: 'wrappedElement'}));
45 | }
46 | });
47 |
48 | hoistNonReactStatics(StoreConnector, Component);
49 |
50 | return StoreConnector;
51 | }
52 |
53 | /**
54 | * Registers change listeners and retrieves state from stores using the `getStateFromStores`
55 | * method. Concept provided by Dan Abramov via
56 | * https://medium.com/@dan_abramov/mixins-are-dead-long-live-higher-order-components-94a0d2f9e750
57 | *
58 | * Example:
59 | * connectToStores(Component, [FooStore], {
60 | * FooStore: function (store, props) {
61 | * return {
62 | * foo: store.getFoo()
63 | * }
64 | * }
65 | * })
66 | *
67 | * Also supports the decorator pattern:
68 | * @connectToStores([FooStore], {
69 | * FooStore: function (store, props) {
70 | * return {
71 | * foo: store.getFoo()
72 | * }
73 | * }
74 | * })
75 | * class ConnectedComponent extends React.Component {
76 | * render() {
77 | * return ;
78 | * }
79 | * }
80 | *
81 | * @method connectToStores
82 | * @param {React.Component} [Component] component to pass state as props to.
83 | * @param {array} stores List of stores to listen for changes
84 | * @param {function} getStateFromStores function that receives all stores and should return
85 | * the full state object. Receives `stores` hash and component `props` as arguments
86 | * @returns {React.Component} or {Function} if using decorator pattern
87 | */
88 | module.exports = function connectToStores(Component, stores, getStateFromStores) {
89 |
90 | // support decorator pattern
91 | if (arguments.length === 2) {
92 | var _stores = Component;
93 | var _getStateFromStores = stores;
94 | return function connectToStoresDecorator(ComponentToDecorate) {
95 | return createComponent(ComponentToDecorate, _stores, _getStateFromStores);
96 | };
97 | }
98 |
99 | return createComponent.apply(null, arguments);
100 | };
101 |
--------------------------------------------------------------------------------
/packages/fluxible/docs/api/Plugins.md:
--------------------------------------------------------------------------------
1 | # API: `Plugins`
2 |
3 | Plugins allow you to extend the interface of each context type.
4 |
5 | ## Creating Plugins
6 |
7 | Here, we'll give components access to the `getFoo()` function:
8 |
9 | ```js
10 | import Fluxible from 'fluxible';
11 | let app = new Fluxible();
12 |
13 | app.plug({
14 | // Required unique name property
15 | name: 'TestPlugin',
16 |
17 | /**
18 | * Called after context creation to dynamically create a context plugin
19 | * @method plugContext
20 | * @param {Object} options Options passed into createContext
21 | * @param {Object} context FluxibleContext instance
22 | * @param {Object} app Fluxible instance
23 | */
24 | plugContext: function (options, context, app) {
25 | // `options` is the same as what is passed into `Fluxible.createContext(options)`
26 | let foo = options.foo;
27 |
28 | // Returns a context plugin
29 | return {
30 | /**
31 | * Method called to allow modification of the component context
32 | * @method plugComponentContext
33 | * @param {Object} componentContext Options passed into createContext
34 | * @param {Object} context FluxibleContext instance
35 | * @param {Object} app Fluxible instance
36 | */
37 | plugComponentContext: function (componentContext, context, app) {
38 | componentContext.getFoo = function () {
39 | return foo;
40 | };
41 | },
42 | //plugActionContext: function (actionContext, context, app) {}
43 | //plugStoreContext: function (storeContext, context, app) {}
44 |
45 | /**
46 | * Allows context plugin settings to be persisted between server and client. Called on server
47 | * to send data down to the client
48 | * @method dehydrate
49 | */
50 | dehydrate: function () {
51 | return {
52 | foo: foo
53 | };
54 | },
55 |
56 | /**
57 | * Called on client to rehydrate the context plugin settings
58 | * @method rehydrate
59 | * @param {Object} state Object to rehydrate state
60 | */
61 | rehydrate: function (state) {
62 | foo = state.foo;
63 | }
64 | };
65 | },
66 |
67 | /**
68 | * Allows dehydration of application plugin settings
69 | * @method dehydrate
70 | */
71 | dehydrate: function () { return {}; },
72 |
73 | /**
74 | * Allows rehydration of application plugin settings
75 | * @method rehydrate
76 | * @param {Object} state Object to rehydrate state
77 | */
78 | rehydrate: function (state) {}
79 | });
80 |
81 | // set a value for it
82 | let context = app.createContext({
83 | foo: 'bar'
84 | });
85 |
86 | // retrieve it from actions, stores or components
87 | context.getActionContext().getFoo(); // returns 'bar'
88 | context.getStoreContext().getFoo(); // returns 'bar'
89 | context.getComponentContext().getFoo(); // returns 'bar'
90 | // or this.context.getFoo() from a React component, read the note below
91 | ```
92 |
93 | ### componentContext
94 |
95 | To leverage the new context value in componentContext, `provideContext` has an option for [passing custom contextTypes](http://fluxible.io/addons/provideContext.html#plugins-and-custom-component-context) which will ensure that it is made available to all children.
96 |
97 | ```
98 | // create the React component as usual
99 | class Application extends React.Component {
100 | render() {
101 | ...
102 | }
103 | }
104 |
105 | // pass in the "foo" context type to register it with React context
106 | Application = provideContext(Application, {
107 | foo: React.PropTypes.string
108 | });
109 |
110 | ```
111 |
112 | Example plugins:
113 | * [fluxible-plugin-fetchr](https://github.com/yahoo/fluxible-plugin-fetchr) - Polymorphic RESTful services
114 | * [Search for more on npm](https://www.npmjs.com/search?q=fluxible+plugin)
115 |
--------------------------------------------------------------------------------
/packages/fluxible/docs/api/Fluxible.md:
--------------------------------------------------------------------------------
1 | # API: `Fluxible`
2 |
3 | Instantiated once for your application, this holds settings and interfaces that are used across all requests.
4 |
5 | ## Usage
6 |
7 | ```js
8 | import Fluxible from 'fluxible';
9 | const fluxibleApp = new Fluxible({
10 | component: require('./components/App.jsx')
11 | });
12 | ```
13 |
14 | For each request:
15 |
16 | ```js
17 | const context = fluxibleApp.createContext();
18 | context.executeAction(action, payload, function () {
19 | const element = React.createElement(context.getComponent(), {
20 | context: context.getComponentContext()
21 | });
22 | const markup = ReactDOM.renderToString(element);
23 | const state = fluxibleApp.dehydrate(context);
24 |
25 | // ... send markup and state to the client ...
26 | });
27 | ```
28 |
29 | ## Methods
30 |
31 | ### Constructor
32 |
33 | Creates a new application instance with the following parameters:
34 |
35 | * `options`: An object containing the application settings
36 | * `options.component` (optional): Stores your top level React component for access using `getComponent()`
37 | * `options.componentActionErrorHandler` (optional): App level component action handler.
38 |
39 | #### `options.component`
40 |
41 | This is a convenience method that allows you to access your component via
42 | `context.getComponent()`. This is useful for when you want to eventually
43 | render your application, you can pass the returned component to your render
44 | method.
45 |
46 | #### `options.componentActionErrorHandler`
47 |
48 | `componentActionErrorHandler` is an action that will be called in the event of
49 | an error being triggered from within an action that was called by a component's
50 | `executeAction`. This is important to handle since components are not able to
51 | pass callbacks to `executeAction`. By default, the application will `throw` the
52 | error but you can override this:
53 |
54 | ```js
55 | const fluxibleApp = new Fluxible({
56 | component: require('./components/App.jsx'),
57 | componentActionErrorHandler: function (context, payload, done) {
58 | var err = payload.err;
59 | // Handle error by setting error state
60 | context.dispatch('APPLICATION_ERROR', err);
61 | done();
62 | }
63 | });
64 | ```
65 |
66 | ### createContext(contextOptions)
67 |
68 | Creates a new [FluxibleContext](FluxibleContext.md) instance passing the `contextOptions` into the constructor. Also iterates over the plugins calling `plugContext` on the plugin if it exists in order to allow dynamic modification of the context.
69 |
70 | ## registerStore(storeClass)
71 |
72 | Registers a [store](Stores.md) to the application making them available to handle actions and be accessible through `getStore` on the [FluxibleContext](FluxibleContext.md).
73 |
74 | ### plug(plugin)
75 |
76 | Allows custom application wide settings to be shared between server and client. Also allows dynamically plugging the [FluxibleContext](FluxibleContext.md) instance each time it is created by implementing a `plugContext` function that receives the context options.
77 |
78 | ### getPlugin(pluginName)
79 |
80 | Provides access to get a plugged plugin by name.
81 |
82 | ### getComponent()
83 |
84 | Provides access to the `options.component` that was passed to the constructor. This is useful if you want to create the application in a file that is shared both server and client but then need to access the top level component in server and client specific files.
85 |
86 | ### dehydrate(context)
87 |
88 | Returns a serializable object containing the state of the Fluxible and passed FluxibleContext instances. This is useful for serializing the state of the application to send it to the client. This will also call any plugins which contain a dehydrate method.
89 |
90 | ### rehydrate(state, callback)
91 |
92 | Takes an object representing the state of the Fluxible and FluxibleContext instances (usually retrieved from dehydrate) to rehydrate them to the same state as they were on the server. This will also call any plugins which contain a rehydrate method. This method is asynchronous to allow for plugins to load necessary assets or data needed for bootstrapping the application. The callback receives the following:
93 |
94 | * `err` - If rehydration had an error
95 | * `context` - A newly created context that is rehydrated to the server state
96 |
--------------------------------------------------------------------------------
/packages/fluxible-addons-react/tests/unit/lib/batchedUpdatePlugin.js:
--------------------------------------------------------------------------------
1 | /*globals describe,it,afterEach,beforeEach,document*/
2 | 'use strict';
3 |
4 | var expect = require('chai').expect;
5 | var mockery = require('mockery');
6 | var React;
7 | var ReactDOM;
8 | var ReactTestUtils;
9 | var connectToStores;
10 | var provideContext;
11 | var FooStore = require('../../fixtures/stores/FooStore');
12 | var BarStore = require('../../fixtures/stores/BarStore');
13 | var Fluxible = require('fluxible');
14 | var jsdom = require('jsdom');
15 |
16 | describe('fluxible-addons-react', function () {
17 | describe('connectToStores', function () {
18 | var appContext;
19 |
20 | beforeEach(function (done) {
21 | jsdom.env('', [], function (err, window) {
22 | if (err) {
23 | done(err);
24 | }
25 | global.window = window;
26 | global.document = window.document;
27 | global.navigator = window.navigator;
28 |
29 | mockery.enable({
30 | warnOnReplace: false,
31 | warnOnUnregistered: false,
32 | useCleanCache: true
33 | });
34 | React = require('react');
35 | ReactDOM = require('react-dom');
36 | ReactTestUtils = require('react-addons-test-utils');
37 | connectToStores = require('../../../').connectToStores;
38 | provideContext = require('../../../').provideContext;
39 |
40 | var batchedUpdatePlugin = require('../../../batchedUpdatePlugin');
41 | var app = new Fluxible({
42 | stores: [FooStore, BarStore]
43 | });
44 | app.plug(batchedUpdatePlugin());
45 |
46 | appContext = app.createContext();
47 |
48 | done();
49 | });
50 | });
51 |
52 | afterEach(function () {
53 | delete global.window;
54 | delete global.document;
55 | delete global.navigator;
56 | mockery.disable();
57 | });
58 |
59 | it('should only call render once when two stores emit changes', function (done) {
60 | var i = 0;
61 | var Component = React.createClass({
62 | componentDidUpdate: function () {
63 | i++;
64 | },
65 | render: function () {
66 | return (
67 |
68 | {this.props.foo}
69 | {this.props.bar}
70 |
71 | );
72 | }
73 | });
74 | var WrappedComponent = provideContext(connectToStores(Component, [FooStore, BarStore], (context) => ({
75 | foo: context.getStore(FooStore).getFoo(),
76 | bar: context.getStore(BarStore).getBar()
77 | })));
78 |
79 | var container = document.createElement('div');
80 | var component = ReactDOM.render(, container);
82 | var wrappedElement = component.refs.wrappedElement.refs.wrappedElement;
83 |
84 | ReactDOM.unstable_batchedUpdates(function () {
85 | wrappedElement.setState({
86 | foo: 'far',
87 | bar: 'baz'
88 | });
89 | wrappedElement.setState({
90 | foo: 'far',
91 | bar: 'baz'
92 | });
93 | });
94 |
95 | expect(i).to.equal(1);
96 | i = 0;
97 |
98 | appContext.executeAction(function (actionContext) {
99 | actionContext.dispatch('DOUBLE_UP');
100 | });
101 | (function checkForFinalState() {
102 | var props = wrappedElement.props;
103 | if (props && props.foo === 'barbar' && props.bar === 'bazbaz') {
104 | if (i > 1) {
105 | done(new Error('Update called ' + i + ' times during dispatch'));
106 | return;
107 | }
108 | done();
109 | } else {
110 | setTimeout(checkForFinalState, 5);
111 | }
112 | })();
113 | });
114 | });
115 | });
116 |
--------------------------------------------------------------------------------
/packages/fluxible/README.md:
--------------------------------------------------------------------------------
1 | # Fluxible
2 |
3 | [](http://badge.fury.io/js/fluxible)
4 |
10 |
11 | Pluggable, singleton-free container for isomorphic [Flux](https://github.com/facebook/flux) applications.
12 |
13 | ```bash
14 | $ npm install --save fluxible
15 | ```
16 |
17 | Join the #fluxible channel of the [Reactiflux](http://reactiflux.com) Discord community.
18 |
19 | [](https://gitter.im/yahoo/fluxible)
20 |
21 | ## Docs
22 |
23 | * [Quick Start](https://github.com/yahoo/fluxible/blob/master/packages/fluxible/docs/quick-start.md)
24 | * [API](https://github.com/yahoo/fluxible/blob/master/packages/fluxible/docs/api/README.md)
25 |
26 | ## Features
27 |
28 | * Singleton-free for server rendering
29 | * [Store](https://github.com/yahoo/fluxible/blob/master/packages/fluxible/docs/api/Stores.md) dehydration for client bootstrapping
30 | * Stateless async [actions](https://github.com/yahoo/fluxible/blob/master/packages/fluxible/docs/api/Actions.md)
31 | * Higher order [components](https://github.com/yahoo/fluxible/blob/master/packages/fluxible/docs/api/Components.md) for easy integration
32 | * Enforcement of Flux flow - restricted access to the [Flux interface](https://github.com/yahoo/fluxible/blob/master/packages/fluxible/docs/api/FluxibleContext.md) from within components
33 | * [Pluggable](https://github.com/yahoo/fluxible/blob/master/packages/fluxible/docs/api/Plugins.md) - add your own interfaces to the Flux context
34 | * Updated for React 0.13
35 |
36 | ## Extras
37 |
38 | * [Yeoman generator](https://github.com/yahoo/generator-fluxible)
39 | * [Example Applications](https://github.com/yahoo/flux-examples)
40 | * [Fluxible Routing](https://github.com/yahoo/fluxible-router)
41 | * [Isomorphic Data Services](https://github.com/yahoo/fluxible-plugin-fetchr)
42 |
43 | ## Usage
44 |
45 | ```js
46 | import Fluxible from 'fluxible';
47 | import {createStore} from 'fluxible/addons';
48 | import {
49 | connectToStores,
50 | createElementWithContext,
51 | provideContext
52 | } from 'fluxible-addons-react';
53 | import React from 'react';
54 | import ReactDOM from 'react-dom/server';
55 |
56 | // Action
57 | const action = (actionContext, payload) => {
58 | actionContext.dispatch('FOO_ACTION', payload);
59 | };
60 |
61 | // Store
62 | const FooStore = createStore({
63 | storeName: 'FooStore',
64 | handlers: {
65 | 'FOO_ACTION': 'fooHandler'
66 | },
67 | initialize: function () { // Set the initial state
68 | this.foo = null;
69 | },
70 | fooHandler: function (payload) {
71 | this.foo = payload;
72 | },
73 | getState: function () {
74 | return {
75 | foo: this.foo
76 | }
77 | }
78 | });
79 |
80 | // Component
81 | class App extends React.Component {
82 | render() {
83 | return {this.props.foo}
84 | }
85 | }
86 |
87 | App = provideContext(connectToStores(App, [FooStore], (context, props) => {
88 | return context.getStore(FooStore).getState();
89 | }));
90 |
91 | // App
92 | const app = new Fluxible({
93 | component: App,
94 | stores: [FooStore]
95 | });
96 |
97 | // Bootstrap
98 | const context = app.createContext();
99 | context.executeAction(action, 'bar', (err) => {
100 | console.log(ReactDOM.renderToString(createElementWithContext(context)));
101 | });
102 | ```
103 |
104 | ## Browser Compatibility
105 |
106 | Fluxible is written with ES2015 in mind and should be used along with polyfills
107 | for features like [`Promise`][Promise] and [`Object.assign`][objectAssign]
108 | in order to support all browsers. We recommend using [Babel][babel] along with
109 | its [polyfill][polyfill].
110 |
111 | ## License
112 |
113 | This software is free to use under the Yahoo Inc. BSD license.
114 | See the [LICENSE file][] for license text and copyright information.
115 |
116 | [Promise]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise
117 | [objectAssign]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign
118 | [babel]: https://babeljs.io/
119 | [polyfill]: https://babeljs.io/docs/usage/polyfill/
120 | [LICENSE file]: https://github.com/yahoo/fluxible/blob/master/LICENSE.md
121 |
--------------------------------------------------------------------------------
/packages/fluxible-addons-react/tests/unit/lib/provideContext.js:
--------------------------------------------------------------------------------
1 | /*globals describe,it*/
2 | 'use strict';
3 |
4 | var expect = require('chai').expect;
5 | var React = require('react');
6 | var ReactDOM = require('react-dom/server');
7 | var provideContext = require('../../..').provideContext;
8 |
9 | describe('fluxible-addons-react', function () {
10 | describe('provideContext', function () {
11 | it('should use the childs name', function () {
12 | var Component = React.createClass({
13 | render: function () {
14 | return null;
15 | }
16 | });
17 |
18 | var WrappedComponent = provideContext(Component);
19 | expect(WrappedComponent.displayName).to.equal('ComponentContextProvider');
20 | });
21 |
22 | it('should use the childs displayName', function () {
23 | var Component = React.createClass({
24 | displayName: 'TestComponent',
25 | render: function () {
26 | return null;
27 | }
28 | });
29 |
30 | var WrappedComponent = provideContext(Component);
31 | expect(WrappedComponent.displayName).to.equal('TestComponentContextProvider');
32 | });
33 |
34 | it('should provide the context with custom types to children', function () {
35 | var context = {
36 | foo: 'bar',
37 | executeAction: function () {
38 | },
39 | getStore: function () {
40 | }
41 | };
42 | var Component = React.createClass({
43 | contextTypes: {
44 | foo: React.PropTypes.string.isRequired,
45 | executeAction: React.PropTypes.func.isRequired,
46 | getStore: React.PropTypes.func.isRequired
47 | },
48 | render: function () {
49 | expect(this.context.foo).to.equal(context.foo);
50 | expect(this.context.executeAction).to.equal(context.executeAction);
51 | expect(this.context.getStore).to.equal(context.getStore);
52 | return null;
53 | }
54 | });
55 | var WrappedComponent = provideContext(Component, {
56 | foo: React.PropTypes.string
57 | });
58 |
59 | ReactDOM.renderToString();
60 | });
61 |
62 | it('should hoist non-react statics to higher order component', function () {
63 | var context = {
64 | foo: 'bar',
65 | executeAction: function () {
66 | },
67 | getStore: function () {
68 | }
69 | };
70 | var Component = React.createClass({
71 | displayName: 'Component',
72 | statics: {
73 | initAction: function () {
74 | }
75 | },
76 | render: function () {
77 | expect(this.context.foo).to.equal(context.foo);
78 | expect(this.context.executeAction).to.equal(context.executeAction);
79 | expect(this.context.getStore).to.equal(context.getStore);
80 | return null;
81 | }
82 | });
83 | var WrappedComponent = provideContext(Component, {
84 | foo: React.PropTypes.string
85 | });
86 |
87 | expect(WrappedComponent.initAction).to.be.a('function');
88 | expect(WrappedComponent.displayName).to.not.equal(Component.displayName);
89 | });
90 |
91 | it('should provide the context with custom types to children using the decorator pattern', function () {
92 | var context = {
93 | foo: 'bar',
94 | executeAction: function () {
95 | },
96 | getStore: function () {
97 | }
98 | };
99 | @provideContext({
100 | foo: React.PropTypes.string
101 | }) class WrappedComponent extends React.Component {
102 | static contextTypes = {
103 | foo: React.PropTypes.string.isRequired,
104 | executeAction: React.PropTypes.func.isRequired,
105 | getStore: React.PropTypes.func.isRequired
106 | };
107 |
108 | render() {
109 | expect(this.context.foo).to.equal(context.foo);
110 | expect(this.context.executeAction).to.equal(context.executeAction);
111 | expect(this.context.getStore).to.equal(context.getStore);
112 | return null;
113 | }
114 | }
115 |
116 | ReactDOM.renderToString();
117 | });
118 | });
119 | });
120 |
--------------------------------------------------------------------------------
/packages/fluxible/docs/api/FluxibleContext.md:
--------------------------------------------------------------------------------
1 | # API: `FluxibleContext`
2 |
3 | Instantiated once per request/session by calling `Fluxible.createContext(options)`, this container provides isolation of stores, dispatches, and other data so that it is not shared between requests on the server side.
4 |
5 | ## SubContexts
6 |
7 | Within a `FluxibleContext`, each component of your application receives a subset of the methods within the `FluxibleContext`. This prevents the components from breaking out of the Flux flow. These subcontexts are modifiable by [plugins](Plugins.md). Each subcontext has a corresponding getter on the `FluxibleContext`, for instance `getComponentContext()`, although for the most part, they will be provided to you within your components without needing to call the getter.
8 |
9 | * [Action Context](Actions.md#action-context) - Passed as first parameter to all actions. Has access to most methods within Fluxible.
10 | * [Component Context](Components.md#component-context) - Passed as a prop to top level React component and then propagated to child components that require access to it.
11 | * [Store Context](Stores.md#store-context) - Passed as first parameter to all store constructors. By default has no methods or properties.
12 |
13 | | | FluxibleContext | Action Context | Component Context | Store Context |
14 | |--------------- |----------------- |---------------- |------------------- |--------------- |
15 | | dispatch | | ✓ | | |
16 | | executeAction | ✓ | ✓ | no callback | |
17 | | getStore | ✓ | ✓ | ✓ | |
18 |
19 | ## Methods
20 |
21 | ### Constructor
22 |
23 | Creates a new context instance with the following parameters:
24 |
25 | * `options`: An object containing the context settings
26 | * `options.app`: Provides access to the application level functions and settings
27 |
28 | ### executeAction(action, payload, [done])
29 |
30 | This is the entry point into an application's execution. The initial action is what begins the flux flow: action dispatches events to stores and stores update their data structures. On the server, we wait for the initial action to finish and then we're ready to render using React. On the client, the components are already rendered and are waiting for store change events.
31 |
32 | Parameters:
33 |
34 | * `action`: A function that takes three parameters: `actionContext`, `payload`, `done`
35 | * `payload`: the action payload
36 | * `done`: optional callback to call when the action has been completed. Receives error as the first parameter and result as the second.
37 |
38 | Returns:
39 |
40 | * `executeActionPromise`: promise that is resolved once the action is completed, or rejected if it has an error
41 |
42 | **Callback**
43 |
44 | ```js
45 | let action = function (actionContext, payload, done) {
46 | // do stuff
47 | done();
48 | };
49 | context.executeAction(action, {}, function (err) {
50 | // action has completed
51 | });
52 | ```
53 |
54 | **Promise**
55 |
56 | ```js
57 | var action = function (actionContext, payload, done) {
58 | // do stuff
59 | done();
60 | };
61 | context.executeAction(action, {})
62 | .then(function (result) {
63 | // action has completed
64 | })
65 | .catch(function (err) {
66 | // action had an error
67 | });
68 | ```
69 |
70 | ### plug(plugin)
71 |
72 | Allows custom context settings to be shared between server and client. Also allows dynamically plugging the ActionContext, ComponentContext, and StoreContext to provide additional methods.
73 |
74 | ### getActionContext()
75 |
76 | Returns the [Action Context](Actions.md#action-context) which provides access to only the functions that should be called from actions. By default: `dispatch`, `executeAction`, and `getStore`.
77 |
78 | This action context object is used every time an `executeAction` method is called and is passed as the first parameter to the action.
79 |
80 | ### getComponentContext()
81 |
82 | Returns the [Component Context](Components.md#Component Context) which provides access to only the functions that should be called from components. By default: `executeAction`, `getStore`. `executeAction` does not allow passing a callback from components so that it enforces actions to be send and forget.
83 |
84 | *Note: You may provide an app level `componentActionErrorHandler` function when instantiating Fluxible. This allows you to catch errors (at a high level) spawning from components firing actions.*
85 |
86 | This context interface should be passed in to your top level React component and then sent down to children as needed. These components will now have access to listen to store instances, execute actions, and access any methods added to the component context by plugins.
87 |
88 | ### getStoreContext()
89 |
90 | Returns the [Store Context](Stores.md#Store Context) which provides access to only the functions that should be called from stores. By default, this is empty, but it is modifiable by plugins.
91 |
92 | ### dehydrate()
93 |
94 | Returns a serializable object containing the state of the `FluxibleContext` and its `Dispatchr` instance. This is useful for serializing the state of the current context to send it to the client. This will also call any plugins whose `plugContext` method returns an object containing a dehydrate method.
95 |
96 | ### rehydrate(state)
97 |
98 | Takes an object representing the state of the `FluxibleContext` and `Dispatchr` instances (usually retrieved from dehydrate) to rehydrate them to the same state as they were on the server. This will also call any plugins whose `plugContext` method returns an object containing a rehydrate method.
99 |
--------------------------------------------------------------------------------
/packages/fluxible/docs/api/Stores.md:
--------------------------------------------------------------------------------
1 | # API: `Stores`
2 |
3 | Flux stores are where you keep your application's state and handle business
4 | logic that reacts to data events. Stores in Fluxible are just classes that
5 | adhere to a simple interface. Because we want stores to be able to be completely
6 | decoupled from Fluxible, we do not provide any store implementation in our
7 | default exports, however you can use the [helper utilities](#helper-utilities)
8 | for creating your stores.
9 |
10 | ```js
11 | import {EventEmitter} from 'events';
12 |
13 | class ApplicationStore extends EventEmitter {
14 | constructor (dispatcher) {
15 | super(dispatcher);
16 | this.dispatcher = dispatcher; // Provides access to waitFor and getStore methods
17 | this.currentPageName = null;
18 | }
19 |
20 | handleReceivePage (payload) {
21 | this.currentPageName = payload.pageName;
22 | this.emit('change');
23 | }
24 |
25 | getCurrentPageName () {
26 | return this.currentPageName;
27 | }
28 |
29 | // For sending state to the client
30 | dehydrate () {
31 | return this.getState();
32 | }
33 |
34 | // For rehydrating server state
35 | rehydrate (state) {
36 | this.currentPageName = state.currentPageName;
37 | }
38 | }
39 |
40 | ApplicationStore.storeName = 'ApplicationStore';
41 | ApplicationStore.handlers = {
42 | 'RECEIVE_PAGE': 'handleReceivePage'
43 | };
44 |
45 | export default ApplicationStore;
46 | ```
47 |
48 | ## Helper Utilities
49 |
50 | Fluxible provides a couple of helpers for building stores with some default
51 | behavior and convenience methods.
52 |
53 | * [BaseStore](addons/BaseStore.md) - Store class that can be extended
54 | * [createStore](addons/createStore.md) - function for creating a class that
55 | extends `BaseStore`
56 |
57 | ## Interface
58 |
59 | ### Constructor
60 |
61 | The store should have a constructor function that will be used to instantiate
62 | your store using `new Store(dispatcher)` where the parameters are as
63 | follows:
64 |
65 | * `dispatcher`: An object providing access to the following methods:
66 | * `dispatcher.getContext()`: Retrieve the [store context](#store-context)
67 | * `dispatcher.getStore(storeClass)`
68 | * `dispatcher.waitFor(storeClass[], callback)`
69 |
70 | The constructor is also where the initial state of the store should be set.
71 |
72 | ```js
73 | class ExampleStore {
74 | constructor (dispatcher) {
75 | this.dispatcher = dispatcher;
76 | if (this.initialize) {
77 | this.initialize();
78 | }
79 | }
80 | }
81 | ```
82 |
83 | It is also recommended to extend an event emitter so that your store can emit
84 | `change` events to the components.
85 |
86 | ```js
87 | class ExampleStore extends EventEmitter {
88 | // ...
89 | }
90 | ```
91 |
92 | ### Static Properties
93 |
94 | #### storeName
95 |
96 | The store should define a static property that gives the name of the store. This
97 | is used internally and for debugging purposes.
98 |
99 | ```js
100 | ExampleStore.storeName = 'ExampleStore';
101 | ```
102 |
103 | #### handlers
104 |
105 | The store should define a static property that maps action names to handler
106 | functions or method names. These functions will be called in the event that an
107 | action has been dispatched by the Dispatchr instance.
108 |
109 | ```js
110 | ExampleStore.handlers = {
111 | 'NAVIGATE': 'handleNavigate',
112 | 'default': 'defaultHandler' // Called for any action that has not been otherwise handled
113 | };
114 | ```
115 |
116 | The handler function will be passed two parameters:
117 |
118 | * `payload`: An object containing action information.
119 | * `actionName`: The name of the action. This is primarily useful when using
120 | the `default` handler
121 |
122 | ```js
123 | class ExampleStore {
124 | handleNavigate (payload, actionName) {
125 | this.navigating = true;
126 | this.emit('change'); // Component may be listening for changes to state
127 | }
128 | }
129 | ```
130 |
131 | If you prefer to define private methods for handling actions, you can use a
132 | static function instead of a method name. This function will be bound to the
133 | store instance when it is called:
134 |
135 | ```js
136 | ExampleStore.handlers = {
137 | 'NAVIGATE': function handleNavigate(payload, actionName) {
138 | // bound to store instance
139 | this.navigating = true;
140 | this.emit('change');
141 | }
142 | };
143 | ```
144 |
145 | ### Instance Methods
146 |
147 | #### dehydrate()
148 |
149 | The store should define this function to dehydrate the store if it will be
150 | shared between server and client. It should return a serializable data object
151 | that will be passed to the client.
152 |
153 | ```js
154 | class ExampleStore {
155 | dehydrate () {
156 | return {
157 | navigating: this.navigating
158 | };
159 | }
160 | }
161 | ```
162 |
163 | #### rehydrate(state)
164 |
165 | The store should define this function to rehydrate the store if it will be
166 | shared between server and client. It should restore the store to the original
167 | state using the passed `state`.
168 |
169 | ```js
170 | class ExampleStore {
171 | rehydrate (state) {
172 | this.navigating = state.navigating;
173 | }
174 | }
175 | ```
176 |
177 | #### shouldDehydrate()
178 |
179 | The store can optionally define this function to control whether the store state
180 | should be dehydrated by the dispatcher. This method should return a boolean. If
181 | this function is undefined, the store will always be dehydrated (just as if true
182 | was returned from method).
183 |
184 | ```js
185 | class ExampleStore {
186 | shouldDehydrate () {
187 | return true;
188 | }
189 | }
190 | ```
191 |
192 | ## Store Context
193 |
194 | The store context by default contains no methods, but can be modified by
195 | plugins.
196 |
--------------------------------------------------------------------------------
/packages/fluxible-reducer-store/docs/api/createReducerStore.md:
--------------------------------------------------------------------------------
1 | # createReducerStore
2 |
3 | ```js
4 | import {createReducerStore} from 'fluxible-reducer-store';
5 | ```
6 |
7 | A helper method for creating reducer stores for Fluxible.
8 |
9 | ## Usage
10 |
11 | ```js
12 | const CountStore = createReducerStore(spec);
13 | ```
14 |
15 | The spec can contain the following keys:
16 |
17 | ### `storeName`
18 |
19 | As with all stores in Fluxible, you will need an identifier for your store.
20 |
21 | ### `reducers` or `reducer`
22 |
23 | This should be a map of `eventName`s that are dispatched through the dispatcher
24 | to the reducer that should handle that event. Reducers should be stateless and
25 | immutable.
26 |
27 | ```js
28 | {
29 | ...spec,
30 | reducers: {
31 | INCREMENT_COUNTER: (state, payload) => {
32 | return {
33 | ...state,
34 | count: state.count + 1
35 | };
36 | }
37 | }
38 | }
39 | ```
40 |
41 | If you prefer to have a single reducer function for all events, you can pass
42 | a single `reducer`:
43 |
44 | ```js
45 | {
46 | ...spec,
47 | reducer: (state, payload, eventName) => {
48 | if ('INCREMENT_COUNTER' === eventName) {
49 | return {
50 | ...state,
51 | count: state.count + 1
52 | };
53 | }
54 | return state;
55 | }
56 | }
57 | ```
58 |
59 | ### `initialState`
60 |
61 | To set up the initial state of your store you can pass an object that will
62 | set the state at instantiation time:
63 |
64 | ```js
65 | {
66 | ...spec,
67 | initialState: {
68 | count: 0
69 | }
70 | }
71 | ```
72 |
73 | ### `getters`
74 |
75 | You can optionally pass a set of getter functions on your store. Getters are
76 | implemented as reducers themselves: they take in state and return the "view"
77 | of that state.
78 |
79 | ```js
80 | {
81 | ...spec,
82 | getters: {
83 | getCount: (state) => {
84 | return state.count;
85 | }
86 | }
87 | }
88 | ```
89 |
90 | You can still call them like you would a getter on a store though:
91 |
92 | ```js
93 | context.getStore(CountStore).getCount();
94 | ```
95 |
96 | This is a convenient way of sharing "views" of your data between several
97 | components.
98 |
99 | ## Full Example
100 |
101 | ```js
102 | const MessageStore = createReducerStore({
103 | storeName: 'MessageStore',
104 | initialState: {
105 | messages: {},
106 | sortedByDate: []
107 | },
108 | reducers: {
109 | RECEIVE_MESSAGES: (state, messages) => {
110 | var oldMessages = state.messages;
111 | var newMessages = {...oldMessages};
112 | messages.forEach(function (message) {
113 | newMessages[message.id] = {
114 | ...message,
115 | isRead: false
116 | };
117 | });
118 | var sortedByDate = (newMessages && Object.keys(newMessages)) || [];
119 | sortedByDate.sort(function (a, b) {
120 | if (newMessages[a].date < newMessages[b].date) {
121 | return -1;
122 | } else if (newMessages[a].date > newMessages[b].date) {
123 | return 1;
124 | }
125 | return 0;
126 | });
127 |
128 | return {
129 | messages: newMessages,
130 | sortedByDate
131 | };
132 | },
133 | OPEN_THREAD: (state, payload) => {
134 | // Mark all read
135 | var oldMessages = state.messages;
136 | var newMessages = {
137 | ...oldMessages
138 | };
139 | Object.keys(state.messages).forEach((key) => {
140 | var message = state.messages[key];
141 | if (message.threadID === payload.threadID) {
142 | newMessages[key] = {
143 | ...message,
144 | text: message.text + 'foo',
145 | isRead: true
146 | };
147 | }
148 | });
149 | return {
150 | ...state,
151 | messages: newMessages
152 | };
153 | }
154 | },
155 | getters: {
156 | getAll: function getAll(state) {
157 | return state.messages;
158 | },
159 | get: function get(state, id) {
160 | return state.messages[id];
161 | },
162 | getAllForThread: function getAllForThread(state, threadID) {
163 | var threadMessages = [];
164 | state.sortedByDate.forEach(function (key) {
165 | var message = state.messages[key];
166 | if (message.threadID === threadID) {
167 | threadMessages.push(message);
168 | }
169 | });
170 | return threadMessages;
171 | }
172 | }
173 | });
174 |
175 | export default MessageStore;
176 | ```
177 |
178 | Now the store can be registered as a normal store.
179 |
180 | ## Hot Reloading
181 |
182 | If you are using webpack's hot module replacement, it is easy to make your
183 | reducer stores hot reloadable. We recommend separating the spec and the call
184 | to `createReducerStore`. For example you could have only the spec in your
185 | store file and call `createReducerStore` in your `app.js`.
186 |
187 | Once you have separated the spec and the store construction, you need to add
188 | the `module.hot.accept` call, require the new spec, and call `Store.replaceSpec`
189 | to ensure that the reducer and getter definitions are correctly replaced.
190 |
191 | _Note: any file that requires your store spec
192 | (e.g. `./stores/MessageStore') will have to implement `module.hot.accept`
193 | including components or actions. You can either implement it in all of your
194 | files or remove the `require`s and call `getStore` with the `storeName` string
195 | instead of the spec/constructor: `context.getStore('MessageStore')`._
196 |
197 | ### Example
198 |
199 | ```js
200 | // ./stores/MessageStore.js
201 | export default {
202 | storeName: 'MessageStore',
203 | reducers: { ...},
204 | initialState: {}
205 | };
206 | ```
207 |
208 | ```js
209 | // ./app.js
210 |
211 | const app = new Fluxible();
212 | const MessageStore = createReducerStore(require('./stores/MessageStore'));
213 |
214 | app.registerStore(MessageStore);
215 |
216 | if (module.hot) {
217 | module.hot.accept('./stores/MessageStore', function () {
218 | var NewMessageStore = require('./stores/MessageStore');
219 | MessageStore.replaceSpec(NewMessageStore);
220 | });
221 | }
222 | ```
223 |
--------------------------------------------------------------------------------
/packages/fluxible/docs/api/Actions.md:
--------------------------------------------------------------------------------
1 | # API: `Actions`
2 |
3 | Actions (called "action creators" in Flux) in Fluxible are stateless functions that receive three parameters:
4 |
5 | * [`actionContext`](#action-context) - Provides access to Flux methods
6 | * `payload` - The action payload
7 | * `done` - A function to be called when the action has been completed
8 |
9 | If the action does not return a promise and takes three parameters, executeAction will wait for the done callback:
10 |
11 | ```js
12 | export default function myAction(actionContext, payload, done) {
13 | setTimeout(function () { // simulate async
14 | actionContext.dispatch('MY_ACTION', payload);
15 | done();
16 | }, 10);
17 | }
18 | ```
19 |
20 | If the action returns a promise, executeAction will wait for it to be resolved or rejected:
21 |
22 | ```js
23 | export default function myPromiseAction(actionContext, payload) {
24 | return getServerData(payload).then(function (data) {
25 | actionContext.dispatch('RECEIVED_SERVER_DATA', data);
26 | });
27 | };
28 | ```
29 |
30 | If the action takes less than three parameters, executeAction will resolve the promise with the return value, if any:
31 |
32 | ```js
33 | export default function mySyncAction(actionContext, payload) {
34 | return actionContext.dispatch('MY_ACTION', payload);
35 | }
36 | ```
37 |
38 | Actions are generally called via [`FluxibleContext.executeAction(myAction, payload, [done])`](FluxibleContext.md#executeactionaction-payload-callback) but actions can also be fired by other actions:
39 |
40 | **Callback**
41 |
42 | ```js
43 | export default function myParentAction(actionContext, payload, done) {
44 | actionContext.executeAction(myAction, payload, done);
45 | }
46 | ```
47 |
48 | **Promise**
49 |
50 | ```js
51 | export default function myParentAction(actionContext, payload) {
52 | return actionContext.executeAction(myAction, payload)
53 | .then(function (result) {
54 | // do something
55 | });
56 | }
57 | ```
58 |
59 |
60 | or from a [component](Components.md):
61 |
62 | ```js
63 | import myAction from './myAction';
64 | class MyComponent extends React.Component {
65 | constructor (props) {
66 | super(props);
67 | this.onClick = this.onClick.bind(this);
68 | }
69 | onClick (e) {
70 | this.context.executeAction(myAction, {});
71 | }
72 | render () {
73 | return