flux - rethink in design pattern
TRANSCRIPT
Flux – Rethink In Design Pattern
JULY, 2015
‹#›CONFIDENTIAL
AGENDA
•What is wrong with MVC?
•Flux in a nutshell
•React as a part of Flux
•What is wrong with Flux?
•Flux frameworks overview
‹#›CONFIDENTIAL
WHAT IS WRONG WITH MVC?
‹#›CONFIDENTIAL
COMMON MVC DIAGRAM
‹#›CONFIDENTIAL
IDEAL MVC APPLICATION
‹#›CONFIDENTIAL
REAL MVC APPLICATION
‹#›CONFIDENTIAL
Models can send events to many views as well as
views too
1
Common two-way binding pattern leads to cascade
changes, which hard to debug
2
No single entry point for actions, so any function can
be publisher or subscriber of event
3
Single responsibility principle is usually become
broken
4
MAIN ISSUES
‹#›CONFIDENTIAL
FLUX IN A NUTSHELL
‹#›CONFIDENTIAL
FLUX COMMON DIAGRAM
(c) http://facebook.github.io/flux/docs/overview.html
‹#›CONFIDENTIAL
DISPATHER
class Dispatcher {
register(callback) {}
unregister(id) {}
waitFor(ids) {}
dispatch(payload) {
for (var id in this._callbacks) {
this._invokeCallback(id);
}
}
_invokeCallback(id) {}
}
‹#›CONFIDENTIAL
STORE
class Store = {
emitChange: function(CHANGE_EVENT) {},
addChangeListener: function(CHANGE_EVENT , callback) {},
removeChangeListener: function(CHANGE_EVENT , callback) {}
};
Dispatcher.register(function(action) {
switch(action.actionType) {
case ACTIONS.ACTION1:
// do something
Store.emitChange(CHANGE_EVENT1);
case ACTIONS.ACTION2:
// do something
Store.emitChange(CHANGE_EVENT2);
}
});
‹#›CONFIDENTIAL
ACTIONS
class Actions = {
action1Function: function(params) {
Dispatcher.dispatch({
actionType: ACTION1,
params: params
});
},
action2Function: function(params) {
Dispatcher.dispatch({
actionType: ACTION2,
params: params
});
}
};
‹#›CONFIDENTIAL
VIEW
class View = {
onEvent1: function() {
Actions.action1Function(params);
},
onEvent2: function() {
Actions.action2Function(params);
}
};
‹#›CONFIDENTIAL
REACT AS A PART OF FLUX
‹#›CONFIDENTIAL
FLUX + REACT WEB DIAGRAM
(c) https://github.com/facebook/flux
‹#›CONFIDENTIAL
Decoupling with component model 1
Virtual DOM, synthetic events and fast render 2
No templates, code and markup in one place3
Uni-directional flow4
‹#›CONFIDENTIAL
REACT BACKBONE
‹#›CONFIDENTIAL
VIRTUAL DOM VS REAL DOM
‹#›CONFIDENTIAL
React View Life Cycle
‹#›CONFIDENTIAL
WHAT IS WRONG WITH FLUX?
‹#›CONFIDENTIAL
ACTIONS list and file grows with complexity of
application
1
class Actions = {
action1Function: function(params) {………………………},
action2Function: function(params) {………………………},
action3Function: function(params) {………………………},
action4Function: function(params) {………………………},
action5Function: function(params) {………………………},
action6Function: function(params) {………………………},
action7Function: function(params) {………………………},
};
‹#›CONFIDENTIAL
When to create new STORE, what are criteria of
STORE as definite entity?
2
Facebook: “Stores are not models, not single records, not collections. Stores manage the application state for a particular domain wi
‹#›CONFIDENTIAL
Can STORE produce ACTION? Does it break the flow?3
class Store = {
doSomething: function() {
Actions.action1Function(params);
}
};
Dispatcher.register(function(action) {
switch(action.actionType) {
case ACTIONS.ACTION1:
Store.doSomething();
}
});
‹#›CONFIDENTIAL
Is it eligible to call two ACTIONS in a row? Does it
break the flow?
4
class View = {
onEvent1: function() {
Actions.action1Function(params);
Actions.action2Function(params);
}
};
‹#›CONFIDENTIAL
Is FLUX framework or pattern?5
Facebook: “Flux is the application architecture that Facebook
uses for building client-side web applications. It complements
React's composable view components by utilizing a
unidirectional data flow. It's more of a pattern rather than a
formal framework”
‹#›CONFIDENTIAL
FLUX FRAMEWORKS OVERVIEW
‹#›CONFIDENTIAL
REFLUX JS
‹#›CONFIDENTIAL
REFLUX JS
(c) https://github.com/spoike/refluxjs
The singleton dispatcher is removed in favor for
letting every action act as dispatcher instead
1
Because actions are listenable, the stores may listen
to them. Stores don't need to have big switch
statements that do static type checking with strings
2
Stores may listen to other stores, i.e. it is possible to
create stores that can aggregate data further,
similar to a map/reduce
3
waitFor is replaced in favor to handle serial and
parallel data flows
4
Action creators are not needed because RefluxJS
actions are functions that will pass on the payload
they receive to anyone listening to them
5
‹#›CONFIDENTIAL
var Actions = Reflux.createActions({
"loadData": {children: ["completedData","failedData"]}
});
Actions.loadData.listen( function() {
ajaxAsyncOperation()
.then( this.completedData )
.catch( this.failedData);
});
var Store = Reflux.createStore({
init: function() {
this.listenToMany(Actions);
},
onCompletedData: function(){},
onFailedData: function(){}
});
var Store = Reflux.createStore({
init: function() {
this.joinLeading(actions.action1,
actions.action2, actions.action3);
}
});
‹#›CONFIDENTIAL
DELOREAN JS
‹#›CONFIDENTIAL
DELOREAN JS
Unidirectional data flow1
Automatically listens to data changes and keeps data
updated
2
Makes data more consistent across whole application3
‹#›CONFIDENTIAL
var Store = DeLorean.Flux.createStore({
setData: function (data) {
this.data = data;
this.emit('change');
},
actions: { 'incoming-data': ‘setData' }
});
var Dispatcher = DeLorean.Flux.createDispatcher({
setData: function (data) {
this.dispatch('incoming-data', data);
},
getStores: function () { return {increment: Store}; }
});
var Actions = {
setData: function (data) {
Dispatcher.setData(data);
}
};
Store.onChange(function () {
document.getElementById('result').innerText = store.data;
});
document.getElementById('dataChanger').onclick = function () {
Actions.setData(Math.random());
};
‹#›CONFIDENTIAL
FLUXXOR
‹#›CONFIDENTIAL
FLUXXOR
Easy to bind actions1
Dispatch function is moved to actions layer2
Built-in integration with React components3
‹#›CONFIDENTIAL
var Store = Fluxxor.createStore({
initialize: function() {
this.bindActions(ACTION1, this.onAction1);
},
onAction1: function(payload) {
//do something
this.emit("change");
}
});
var actions = {
action1: function(payload) {
this.dispatch(ACTION1, payload);
}
};
var FluxMixin = Fluxxor.FluxMixin(React), StoreWatchMixin =
Fluxxor.StoreWatchMixin;
var Application = React.createClass({
mixins: [FluxMixin, StoreWatchMixin("Store")],
getStateFromFlux: function() {
return this.getFlux().store("Store").getState();
},
render: function () {}
});
‹#›CONFIDENTIAL
REFERENCE
https://github.com/facebook/flux
http://facebook.github.io/react/blog/2014/05/06/flux.html
https://facebook.github.io/react/docs/component-specs.html
https://github.com/voronianski/flux-comparison
https://www.youtube.com/watch?v=LTj4O7WJJ98
‹#›CONFIDENTIAL
THANKQ
Q&A