Skip to content
This repository has been archived by the owner on Nov 10, 2017. It is now read-only.

Commit

Permalink
Explicit action subscription in stores.
Browse files Browse the repository at this point in the history
  • Loading branch information
n1k0 committed Feb 2, 2015
1 parent 31738bd commit d7e3756
Show file tree
Hide file tree
Showing 6 changed files with 343 additions and 193 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
node_modules
npm-debug.log
coverage
124 changes: 56 additions & 68 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,60 +8,21 @@ Read [more about Flux here](http://facebook.github.io/flux/docs/overview.html).
Dispatcher
----------

That's rather simple:
Essential, central piece of the Flux architecture, the Dispatcher registers and dispatches action events.

```js
var Dispatcher = DocBrown.createDispatcher();
```

Stores
------

### Definition
Creating a dispatcher is rather simple:

```js
var TimeStore = DocBrown.createStore({
getInitialState: function() {
return {year: 2015};
}
});
```

### Usage

```js
var store = new TimeStore();

console.log(store.getState().year); // 2015

store.subscribe(function(state) {
console.log(state.year); // 1995
console.log(state === store.getState()); // true
});
var Dispatcher = DocBrown.createDispatcher();

store.setState({year: 1995})
Dispatcher.dispatch("foo");
```

### Registering

Stores need to be registered against the Dispatcher, so it can notify subscribers from state change.

```js
var timeStore = new TimeStore();
var plutoniumStore = new PlutoniumStore();

// Register stores to be notified by action events.
Dispatcher.register({
timeStore: timeStore,
plutoniumStore: plutoniumStore
});
```
Most of the time, you'll never have to call anything from the Dispatcher; Actions will.

Actions
-------

### Definition

Actions are defined using an array of strings, where entries are action names. Actions are responsible of dispatching events on their own, that's why they need to know about the dispatcher.

```js
Expand All @@ -70,61 +31,87 @@ var TimeActions = DocBrown.createActions(Dispatcher, [
"backward",
"forward"
]);

typeof TimeActions.backward; // "function"
typeof TimeActions.forward; // "function"

TimeActions.forward(); // dispatches a "forward" action event.
```

### Conventions
**Note:** Arguments passed to action functions are applied to their matching store methods.

- The name of the action should match the one of the store which should be called;
- Args passed to the action function are applied to the store method.
Stores
------

A store reflects the current state of a given application domain data. It:

- defines initial state;
- alters state;
- subscribes to action events and optionnaly react accordingly (eg. by altering state);
- notifies subscribers from state change events.

```js
var Dispatcher = DocBrown.createDispatcher();
var TimeActions = DocBrown.createActions(Dispatcher, [
"backward",
"forward"
]);
var TimeStore = DocBrown.createStore({
actions: [TimeActions],
getInitialState: function() {
return {year: 2015};
},
backward: function(years) {
this.setState({year: this.getState().year - years});
backward: function() {
this.setState({year: this.getState().year - 1});
},
forward: function() {
this.setState({year: this.getState().year + 1});
},
forward: function(years) {
this.setState({year: this.getState().year + years});
}
});

// Usage
var store = new TimeStore();

Dispatcher.register({timeStore: timeStore});
console.log(store.getState().year); // 2015

timeStore.subscribe = function(state) {
console.log(state.year, state === store.getState());
};
store.subscribe(function(state) {
console.log(state.year); // 2016
console.log(state === store.getState()); // true
});

Action.forward(20); // 2035, true
Action.backward(20); // 1995, true
store.forward();
```

## Asynchronous actions
### Asynchronous actions

**There's no such thing as async actions.** Let's keep the initial need simple and iron out the problem; an asynchronous operation should first call a sync action and then make the store triggering new actions dedicated to handle successes and failures:
**There are no such things as async actions.** Let's keep the initial need simple and iron out the problem; an asynchronous operation should first call a sync action and then make the store triggering new actions dedicated to handle successes and failures:

```js
var TimeActions = DocBrown.createActions(Dispatcher, [
"travelBackward",
"travelBackwardStarted",
"travelBackwardSucceeded",
"travelBackwardFailed"
]);

var TimeStore = DocBrown.createStore({
actions: [TimeActions],
getInitialState: function() {
return {year: 2015, error: null};
},
travelBackward: function(years) {
TimeActions.travelBackwardStarted(years);
setTimeout(function() {
if (Math.random() > .5) {
Actions.travelBackwardSucceeded(this.getState().years - years);
TimeActions.travelBackwardSucceeded(this.getState().years - years);
} else {
Actions.travelBackwardFailed(new Error("Damn."));
TimeActions.travelBackwardFailed(new Error("Damn."));
}
}.bind(this), 50);
},
travelBackwardStarted: function(years) {
console.warn("Ignition.");
},
travelBackwardSucceeded: function(newYear) {
this.setState({year: newYear});
},
Expand All @@ -137,16 +124,17 @@ var TimeStore = DocBrown.createStore({
React mixin
===========

This implementation isn't tied to [React](facebook.github.io/react/), though a React mixin is provided. A demo is available in the `demo/` directory.
This Flux implementation isn't tied to [React](facebook.github.io/react/), though a React mixin is conveniently provided.

Basic usage:

```js
var Dispatcher = DocBrown.createDispatcher();

var Actions = DocBrown.createActions(Dispatcher, ["travelBy"]);
var TimeActions = DocBrown.createActions(Dispatcher, ["travelBy"]);

var TimeStore = DocBrown.createStore({
actions: [TimeActions],
getInitialState: function() {
return {year: new Date().getFullYear()};
},
Expand All @@ -155,14 +143,12 @@ var TimeStore = DocBrown.createStore({
}
});

Dispatcher.register({timeStore: new TimeStore()});

var Counter = React.createClass({
mixins: [DocBrown.storeMixin(Dispatcher, "timeStore")],
mixins: [DocBrown.storeMixin(timeStore)],

travelClickHandler: function(years) {
return function() {
Actions.travelBy(years);
TimeActions.travelBy(years);
};
},

Expand All @@ -178,6 +164,8 @@ var Counter = React.createClass({
React.render(<Counter/>, document.body);
```

A working demo is available in the `demo/` directory in this repository.

Install
=======

Expand Down
9 changes: 4 additions & 5 deletions demo/demo.jsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
var Dispatcher = DocBrown.createDispatcher();

var Actions = DocBrown.createActions(Dispatcher, ["travelBy"]);
var TimeActions = DocBrown.createActions(Dispatcher, ["travelBy"]);

var TimeStore = DocBrown.createStore({
actions: [TimeActions],
getInitialState: function() {
return {year: new Date().getFullYear()};
},
Expand All @@ -11,14 +12,12 @@ var TimeStore = DocBrown.createStore({
}
});

Dispatcher.register({timeStore: new TimeStore()});

var Counter = React.createClass({
mixins: [DocBrown.storeMixin(Dispatcher, "timeStore")],
mixins: [DocBrown.storeMixin(new TimeStore())],

travelClickHandler: function(years) {
return function() {
Actions.travelBy(years);
TimeActions.travelBy(years);
};
},

Expand Down
Loading

0 comments on commit d7e3756

Please sign in to comment.