Flux
"React Goes Big"
React: Declarative Render
var NumberSelector = React.createClass({
render: function() {
return (
<div>
<p>You selected: Nothing</p>
<select>
<option>1</option>
<option>2</option>
<option>3</option>
</select>
</div>
)
},
})
React: Component State
var NumberSelector = React.createClass({
getInitialState: function() {
return {
selectedValue: 1
}
},
render: function() {
return (
<div>
<p>You selected: {this.state.selectedValue}</p>
<select value={this.state.selectedValue}>
<option>1</option>
<option>2</option>
<option>3</option>
</select>
</div>
)
},
})
React: Modifying State
var NumberSelector = React.createClass({
getInitialState: function() {
return {
selectedValue: 1
}
},
render: function() {
return (
<div>
<p>You selected: {this.state.selectedValue}</p>
<select
value={this.state.selectedValue}
onChange={this.handleChange}
>
<option>1</option>
<option>2</option>
<option>3</option>
</select>
</div>
)
},
handleChange: function(ev) {
this.setState({
selectedValue: ev.target.value
})
}
})
React Recap
- State lives in one place
- Rendered view derived from state
- Explicit actions modify state
One-way data flow inside a component
Flux: React Goes Big
State
DOM
Tree
Events
Stores
View
(React.js)
Actions
Actions
View
(React)
Flux: Stores
var SelectedValueStore = {
}
/* storage */
_value: 1,
getValue: function() {
return this._value
},
/* event emitter */
_handlers: [],
addChangeHandler: function(func) {
this._handlers.push(func)
},
removeChangeHandler: function(func) {
var idx = this._handlers.indexOf(func)
this._handlers.splice(idx, 1)
},
emitChange: function() {
this._handlers.forEach(function(func){ func() })
},
- Expose data
via getter functions
- Notify subscribers
of changes
- DON'T expose setters
- DON'T fetch data
Flux: Stores
var NumberSelector = React.createClass({
})
- Read app state
- Subscribe to
changes
/* subscribe to change events (and unsubscribe): */
componentWillMount: function() {
SelectedValueStore.addChangeHandler(this._handleValueChanged)
},
componentWillUnmount: function() {
SelectedValueStore.removeChangeHandler(this._handleValueChanged)
},
/* read state from store: */
getInitialState: function() {
return {
selectedValue: SelectedValueStore.getValue()
}
},
render: function() {
/* render stays the same :D */
},
/* handle change events by re-reading from store: */
_handleValueChanged: function() {
this.setState({
selectedValue: SelectedValueStore.getValue()
})
},
Flux: Actions
/* this is Facebook's dispatcher */
var valueDispatcher = new Flux.Dispatcher()
var ValueSelectionActions = {
/* this function creates a VALUE_WAS_SELECTED action: */
valueWasSelected: function(value) {
},
}
- Broadcasted by a dispatcher
- POJO with `actionType`, at least
var action = {
actionType: "VALUE_WAS_SELECTED",
value: value,
}
valueDispatcher.dispatch(action)
Flux: Actions
/* make a handler */
var SelectedValueStore = {
// ...
_actionHandler: function(payload) {
if (payload.actionType == "VALUE_WAS_SELECTED") {
this._value = payload.value // update state
this.emitChange() // broadcast changes
}
},
}
- Stores handle actions
- Callbacks are registered on the dispatcher
/* hook up the store */
var boundCallback = SelectedValueStore._eventHandler.bind(SelectedValueStore)
valueDispatcher.register(boundCallback)
Flux: Actions
/* trigger the action from the view: */
var NumberSelector = React.createClass({
// ...
handleChange: function(ev) {
},
})
Views trigger application actions in response to user input
// don't set local state,
// instead, trigger event for the whole application
ValueSelectionActions.valueWasSelected(ev.target.value)
Flux
Flux Implementations
- Facebook's dispatcher (facebook/flux)
- Facebook's event emitter (facebook/emitter)
- McFly (kenwheeler/mcfly)
- Flummox (acdlite/flummox, ES6-friendly)
Flux Lab
Your Questions?
Robert Mosolgo
Full Stack, March 12, 2015
Flux
By Robert Mosolgo
Flux
Flux from the angle of: "Flux is React's one-way data flow, but bigger"
- 1,444