Flux is the application architecture developed by Facebook for building client-side web applications with unidirectional data flow.
Architecture Overview
┌───────────────┐
│ Action Creator│ ───> Action ───┐
└───────────────┘ │
▼
┌───────┐ ┌────────────┐
│ View │ <── Change Event ─── │ Store │ <─── Dispatch(Action)
└───────┘ └────────────┘
│ ▲
└──────── Action Trigger ───────┘
- Dispatcher: Single central hub that receives actions and broadcasts them to registered store callbacks.
- Stores: Hold application state and business logic, updating state upon receiving dispatched actions.
- Views: React components listening to store change events and requesting new actions through action creators.
Dispatcher
Registering and Dispatching
const { Dispatcher } = require('flux');
const AppDispatcher = new Dispatcher();
// Register a store callback; returns a token used for waitFor()
const token = AppDispatcher.register(function (payload) {
switch (payload.actionType) {
case 'TODO_CREATE':
TodoStore.create(payload.text);
TodoStore.emitChange();
break;
}
});
// Broadcast action payload to all stores
AppDispatcher.dispatch({
actionType: 'TODO_CREATE',
text: 'Learn Flux'
});
Sequencing Stores with waitFor()
const FlightStore = {
dispatcherIndex: AppDispatcher.register(function (payload) {
switch (payload.actionType) {
case 'BUY_TICKET':
// Ensure CityStore finishes updating before FlightStore runs
AppDispatcher.waitFor([CityStore.dispatcherIndex]);
FlightStore.country = CityStore.getCountry();
break;
}
})
};
AppDispatcher.waitFor(tokens) pauses execution of the current callback until the callbacks corresponding to the specified tokens have run.
Stores
Plain Store Structure with EventEmitter
const EventEmitter = require('events').EventEmitter;
const AppDispatcher = require('../dispatcher/AppDispatcher');
const _todos = {};
function create(text) {
const id = Date.now();
_todos[id] = { id, text, complete: false };
}
const TodoStore = Object.assign({}, EventEmitter.prototype, {
getAll: function () {
return _todos;
},
emitChange: function () {
this.emit('change');
},
addChangeListener: function (callback) {
this.on('change', callback);
},
removeChangeListener: function (callback) {
this.removeListener('change', callback);
}
});
TodoStore.dispatchToken = AppDispatcher.register(function (action) {
switch (action.type) {
case 'TODO_CREATE':
create(action.text);
TodoStore.emitChange();
break;
}
});
module.exports = TodoStore;
View Integration (React)
const React = require('react');
const TodoStore = require('../stores/TodoStore');
const TodoActions = require('../actions/TodoActions');
class TodoApp extends React.Component {
constructor(props) {
super(props);
this.state = { todos: TodoStore.getAll() };
this._onChange = this._onChange.bind(this);
}
componentDidMount() {
TodoStore.addChangeListener(this._onChange);
}
componentWillUnmount() {
TodoStore.removeChangeListener(this._onChange);
}
_onChange() {
this.setState({ todos: TodoStore.getAll() });
}
render() {
return (
<div>
<button onClick={() => TodoActions.create("New task")}>
Add Todo
</button>
</div>
);
}
}