Here’s what we’re building…

screenshot

and here’s how it’s built:

Actions

Create an object with three actions:

Actions = Reflux.createActions [
  "up",
  "down",
  "setToValue"
]

Actions can be fired and listened to. When fired, the arguments are forwarded to all listeners. For example:

# attach listener to the "set" action
Actions.setToValue.listen (n) ->
  console.log("setToValue #{n}")

# fire the "setToValue" action; outputs "setToValue 3"
Actions.setToValue(3)

Stores

Each store manages part of the application’s state. Here there is only one store: NumberStore.

Stores listen to actions and manage state accordingly. Actions serve as an API for interacting with stores. In fact, actions are intended to be the only way to interact with stores.

Stores call trigger whenever they want to notify subscribers of a change in state. (Components listen for these updates and update their state accordingly; more on this later.)

Here’s our NumberStore:

NumberStore = Reflux.createStore
  init: ->
    @_number = 0
    @listenToMany(Actions)

  getInitialState: ->
    @_number

  onUp: ->
    @_number += 1
    @trigger(@_number)

  onDown: ->
    @_number -= 1
    @trigger(@_number)

  onSet: (n) ->
    @_number = n
    @trigger(@_number)

The init method initializes the store’s state and wires listeners to actions. RefluxJS’s listenToMany method automatically wires up handlers based on their names:

  • up is wired to onUp
  • down is wired to onDown
  • setToValue is wired to onSetToValue

The getInitialState method is used for integration with React components. We’ll get to that later.

Integration with React Components

RefluxJS provides a mixin generator to connect a stores’s state to a React component:

TopLevelView = React.createClass
  mixins: [ Reflux.connect(NumberStore, "number") ]

  render: ->
    `(
      <div>
        <NumberCounter number={this.state.number} />
        <AsteriskCounter number={this.state.number} />
        <br />
        <button onClick={this._setToFour}>
          Four
        </button>
      </div>
    )`

  _setToFour: ->
    Actions.setToValue(4)

The mixin returned by Reflux.connect(NumberStore, "number") automatically subscribes the component to NumberStore when the component mounts and unsubscribes when it unmounts. It ensures that any time NumberStore triggers a state change, that new state is automatically set in the component’s state using the “number” property.

Furthermore, because we implemented getIntialState in NumberStore, the mixin also ensures that the state will be set prior to the initial rendering.

Interacting with Stores

React components interact with stores by calling actions. For example, in the above component, Action.setToValue(4) is called in response to clicking the “Four” button. When this action is called, the following sequence of events happens:

  1. NumberStore, having registered a listener to the “setToValue” action, sets it’s state to 4.
  2. NumberStore calls trigger with the new state.
  3. Having included the Reflux.connect mixin, the component calls setState(number: 4).
  4. The component is re-rendered and the DOM is refreshed.

The sequence of events is similiar for both sets of up and down buttons.

Full Source Code

Here’s the full source code. Components call actions which are listened to by stores. Stores trigger state changes in top level components, and that state is pushed down to subcomponents as props:

Actions = Reflux.createActions [
  "up",
  "down",
  "setToValue"
]


NumberStore = Reflux.createStore
  init: ->
    @_number = 0
    @listenToMany(Actions)

  getInitialState: ->
    @_number

  onUp: ->
    @_number += 1
    @trigger(@_number)

  onDown: ->
    @_number -= 1
    @trigger(@_number)

  onSet: (n) ->
    @_number = n
    @trigger(@_number)


TopLevelView = React.createClass
  mixins: [ Reflux.connect(NumberStore, "number") ]

  render: ->
    `(
      <div>
        <NumberCounter number={this.state.number} />
        <AsteriskCounter number={this.state.number} />
        <br />
        <button onClick={this._setToFour}>
          Four
        </button>
      </div>
    )`

  _setToFour: ->
    Actions.setToValue(4)


NumberCounter = React.createClass
  render: ->
    `(
      <p>
        <button onClick={this._up}>&uarr;</button>
        <button onClick={this._down}>&darr;</button>
        <span style=>
          {this.props.number}
        </span>
      </p>
    )`

  _up: ->
    Actions.up()

  _down: ->
    Actions.down()


AsteriskCounter = React.createClass
  render: ->
    `(
      <p>
        <button onClick={this._up}>&uarr;</button>
        <button onClick={this._down}>&darr;</button>
        <span style=>
          {this._asterisks()}
        </span>
      </p>
    )`

  _asterisks: ->
    Array(@props.number + 1).join(" * ")

  _up: ->
    Actions.up()

  _down: ->
    Actions.down()