MobX
![](https://s3.amazonaws.com/media-p.slid.es/uploads/52782/images/2916162/mobx.png)
MobX
![](https://s3.amazonaws.com/media-p.slid.es/uploads/52782/images/2916162/mobx.png)
Externals
About The title
![](http://images-cdn.moviepilot.com/image/upload/c_fill,h_381,t_mp_quality,w_920/-a8d6ad6e-07b5-45eb-ad8b-89dcde7029f5.jpg)
"Revenge Of The Observable!"
whomai
vitali.pe@gmail.com
![](https://s3.amazonaws.com/media-p.slid.es/uploads/52782/images/1791460/10005910_10202959551337011_933565341_o.jpg)
day 0
whomai
vitali.pe@gmail.com
![](https://s3.amazonaws.com/media-p.slid.es/uploads/52782/images/1791460/10005910_10202959551337011_933565341_o.jpg)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/52782/images/2917751/IMG-20160615-WA0000.jpg)
day 0
day 1546
MobX
![](https://s3.amazonaws.com/media-p.slid.es/uploads/52782/images/2916162/mobx.png)
MobX
A small library to manage state changes automatically
![](https://s3.amazonaws.com/media-p.slid.es/uploads/52782/images/2916162/mobx.png)
MobX
A small library to manage state changes automatically
![](https://s3.amazonaws.com/media-p.slid.es/uploads/52782/images/2916162/mobx.png)
MobX
A small library to manage state changes automatically
![](https://s3.amazonaws.com/media-p.slid.es/uploads/52782/images/2916162/mobx.png)
What Is "state"?
What Is "state"?
the particular condition that something is in at a specific time
What Is "state"?
the particular condition that something is in at a specific time
What Is "state"?
the particular condition that something is in at a specific time
What Is "state"?
the particular condition that something is in at a specific time
structure + changes
data
What Is "state"?
the particular condition that something is in at a specific time
structure + changes
data
What Is "state"?
the particular condition that something is in at a specific time
structure + changes
data
![](https://s3.amazonaws.com/media-p.slid.es/uploads/52782/images/2174496/eGHU2IZ.gif)
React
![](https://s3.amazonaws.com/media-p.slid.es/uploads/vitaliperchonok/images/771217/react.png)
Solves This Problem For UI State
Kinda...
UI Tree
![](https://s3.amazonaws.com/media-p.slid.es/uploads/vitaliperchonok/images/771217/react.png)
UI Tree
![](https://s3.amazonaws.com/media-p.slid.es/uploads/vitaliperchonok/images/771217/react.png)
UI Tree
![](https://s3.amazonaws.com/media-p.slid.es/uploads/vitaliperchonok/images/771217/react.png)
UI Tree
![](https://s3.amazonaws.com/media-p.slid.es/uploads/vitaliperchonok/images/771217/react.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/52782/images/2917751/IMG-20160615-WA0000.jpg)
Problem:
Problem:
Problem:
any nontrivial app state is a graph
TodoMVC
TodoMVC
Not Only React
![](https://s3.amazonaws.com/media-p.slid.es/uploads/52782/images/2170523/square.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/vitaliperchonok/images/772010/Backbone_logo_logo_only__1_.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/52782/images/2917068/GTK-3-9-8-Brings-Major-Changes-in-Preparation-for-GTK-4-2.jpg)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/52782/images/2917069/Qt-Tizen-TizenExperts.jpg)
Ideas
1. Manually compose data in the tree.
Ideas
1. Manually compose data in the tree.
Ideas
1. Manually compose data in the tree.
2. Use a DSL to query for data.
Use a DSL to query for data.
(for example GraphQL)
{
user(id: 1) {
age
friends {
name
}
}
}
UI Tree
{
user(id: 42) {
name
friends {
age
}
}
}
-
String based DSL
-
No clear schema (by design)
-
Works well on dense graphs
-
Solves a more general problem
Ideas
1. Manually compose data in the tree.
2. Use a DSL to query for data.
3. Save state outside of the tree.
Save state outside of the tree.
A.K.A "The Reagent Way"
(def click-count (r/atom 0))
(defn counting-component []
[:div
"The atom " [:code "click-count"] " has value: "
@click-count ". "
[:input {:type "button" :value "Click me!"
:on-click #(swap! click-count inc)}]])
Save state outside of the tree.
A.K.A "The Reagent Way"
(def click-count (r/atom 0))
(defn counting-component []
[:div
"The atom " [:code "click-count"] " has value: "
@click-count ". "
[:input {:type "button" :value "Click me!"
:on-click #(swap! click-count inc)}]])
Ideas
1. Manually compose data in the tree.
2. Use a DSL to query for data.
3. Save state outside of the tree.
4. Make it look like a tree.
Make it look like a tree
UI Tree
Make it look like a tree
"ViewModel" in MVVM
($scope)
"Cursors" (om, Reagent,etc)
(def foo-cursor (cursor app-state [:foo]))
(defn inside-foo-cursor []
[:div (str "Inside foo-cursor: " @foo-cursor)])
MobX
![](https://s3.amazonaws.com/media-p.slid.es/uploads/52782/images/2916162/mobx.png)
A small library to manage state changes automatically
MobX
![](https://s3.amazonaws.com/media-p.slid.es/uploads/52782/images/2916162/mobx.png)
4 simple consepts, no magic
![](https://s3.amazonaws.com/media-p.slid.es/uploads/52782/images/2917136/flow.png)
Observable State
var me = mobx.observable({
firstName : "vitali",
lastName : "Perchonok",
favoriteNumbers : [0],
age : 29
})
![](https://s3.amazonaws.com/media-p.slid.es/uploads/52782/images/2916162/mobx.png)
Observable State
var me = mobx.observable({
firstName : "vitali",
lastName : "Perchonok",
favoriteNumbers : [0],
age : 29
})
mobx.autorun(
() => console.log("your name:",
me.firstName, me.lastName))
![](https://s3.amazonaws.com/media-p.slid.es/uploads/52782/images/2916162/mobx.png)
Observable State
var me = mobx.observable({
firstName : "vitali",
lastName : "Perchonok",
favoriteNumbers : [0],
age : 29
})
mobx.autorun(
() => console.log("your name:",
me.firstName, me.lastName))
![](https://s3.amazonaws.com/media-p.slid.es/uploads/52782/images/2916162/mobx.png)
Observable State
var me = mobx.observable({
firstName : "vitali",
lastName : "Perchonok",
favoriteNumbers : [0],
age : 29
})
mobx.autorun(
() => console.log("your name:",
me.firstName, me.lastName))
![](https://s3.amazonaws.com/media-p.slid.es/uploads/52782/images/2916162/mobx.png)
me.fullName = "Other";
// console: your name: Other Perchonok
me.lastName = "Name";
// console: your name: Other Name
me.age = 30;
me.favoriteNumbers.push(12);
// no effect
![](https://s3.amazonaws.com/media-p.slid.es/uploads/52782/images/2916162/mobx.png)
Observable State
Computed State
var me = mobx.observable({
....
fullName : function() {
return [this.firstName, this.lastName].join(" ")
}
})
![](https://s3.amazonaws.com/media-p.slid.es/uploads/52782/images/2916162/mobx.png)
Computed State
var me = mobx.observable({
....
fullName : function() {
return [this.firstName, this.lastName].join(" ")
}
})
mobx.autorun(() => console.log("your name:", me.fullName))
![](https://s3.amazonaws.com/media-p.slid.es/uploads/52782/images/2916162/mobx.png)
Computed State
var me = mobx.observable({
....
fullName : function() {
return [this.firstName, this.lastName].join(" ")
}
})
mobx.autorun(() => console.log("your name:", me.fullName))
![](https://s3.amazonaws.com/media-p.slid.es/uploads/52782/images/2916162/mobx.png)
Computed State
var me = mobx.observable({
....
fullName : function() {
return [this.firstName, this.lastName].join(" ")
}
})
mobx.autorun(() => console.log("your name:", me.fullName))
![](https://s3.amazonaws.com/media-p.slid.es/uploads/52782/images/2916162/mobx.png)
Computed State
var me = mobx.observable({
....
fullName : function() {
return [this.firstName, this.lastName].join(" ")
}
})
mobx.autorun(() => console.log("your name:", me.fullName))
![](https://s3.amazonaws.com/media-p.slid.es/uploads/52782/images/2916162/mobx.png)
Computed State
var me = mobx.observable({
....
fullName : function() {
console.log("computed full name!");
return [this.firstName, this.lastName].join(" ")
}
})
mobx.autorun(() => console.log("your name:", me.fullName))
![](https://s3.amazonaws.com/media-p.slid.es/uploads/52782/images/2916162/mobx.png)
me.fullName = "Other";
// console: computed full name!
// console: your name: Other Perchonok
me.lastName = "Name";
// console: computed full name!
// console: your name: Other Name
me.fullName;
me.fullName;
// no effect
![](https://s3.amazonaws.com/media-p.slid.es/uploads/52782/images/2916162/mobx.png)
Computed State
me.fullName = "Other";
// console: computed full name!
// console: your name: Other Perchonok
me.lastName = "Name";
// console: computed full name!
// console: your name: Other Name
me.fullName;
me.fullName;
// no effect
![](https://s3.amazonaws.com/media-p.slid.es/uploads/52782/images/2916162/mobx.png)
Computed State
var me = mobx.observable({
...
rename : action(function (first, last) {
this.firstName = first;
this.lastName = last;
})
})
Actions
![](https://s3.amazonaws.com/media-p.slid.es/uploads/52782/images/2916162/mobx.png)
var me = mobx.observable({
...
rename : action("rename user",
function (first, last) {
this.firstName = first;
this.lastName = last;
})
})
Actions
![](https://s3.amazonaws.com/media-p.slid.es/uploads/52782/images/2916162/mobx.png)
var me = mobx.observable({
...
rename : action("rename user",
function (first, last) {
this.firstName = first;
this.lastName = last;
})
})
Actions
![](https://s3.amazonaws.com/media-p.slid.es/uploads/52782/images/2916162/mobx.png)
me.rename("Other", "name");
// console: computed full name!
// console: your name: Other Name
me.fullName;
me.fullName;
// no effect
Actions
![](https://s3.amazonaws.com/media-p.slid.es/uploads/52782/images/2916162/mobx.png)
me.rename("Other", "name");
// console: computed full name!
// console: your name: Other Name
mobx.useStrict(true);
me.firstName = "boo"; // throws:
// [mobx] Invariant failed: It is not allowed to
// create or change state outside an `action`
Actions
![](https://s3.amazonaws.com/media-p.slid.es/uploads/52782/images/2916162/mobx.png)
class Person {
@observable firstName = "vitali";
@observable lastName = "Perchonok";
...
@computed get fullName() {
return [this.firstName, this.lastName].join(" ");
}
@action("rename user") rename(first, last) {
this.firstName = first;
this.lastName = last;
}
}
![](https://s3.amazonaws.com/media-p.slid.es/uploads/52782/images/2916162/mobx.png)
ES6+
MobX
![](https://s3.amazonaws.com/media-p.slid.es/uploads/52782/images/2916162/mobx.png)
4 simple consepts, no magic
![](https://s3.amazonaws.com/media-p.slid.es/uploads/52782/images/2917136/flow.png)
Simple things should be simple, complex things should be possible.
Alan Kay
![](http://history-computer.com/ModernComputer/Personal/images/alan-kay-and-dynabook.jpg)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/52782/images/2916162/mobx.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/vitaliperchonok/images/771229/react.png)
+
"mobx-react"
class Crap extends React.Component {
render() {
return <Layout>
<Component data={this.props.user}/>
<SimpleWidget />
</layout>
}
}
export default observer(Crap);
![](https://s3.amazonaws.com/media-p.slid.es/uploads/52782/images/2916162/mobx.png)
observer()
class Crap extends React.Component {
render() {
return <Layout>
<Component data={this.props.user}/>
<SimpleWidget />
</layout>
}
}
export default observer(Crap);
![](https://s3.amazonaws.com/media-p.slid.es/uploads/52782/images/2916162/mobx.png)
observer()
var StatelessCrap = observer(({user}) => {
return <Layout>
<Component data={user}/>
<SimpleWidget />
</layout>
})
export default StatelessCrap ;
![](https://s3.amazonaws.com/media-p.slid.es/uploads/52782/images/2916162/mobx.png)
observer()
@observer
class Crap extends React.Component {
render() {
return <Layout>
<Component data={this.props.user}/>
<SimpleWidget />
</layout>
}
}
export default Crap;
![](https://s3.amazonaws.com/media-p.slid.es/uploads/52782/images/2916162/mobx.png)
@observer
@observer
class Crap extends React.Component {
render() {
return <Layout>
<Component data={this.props.user}/>
<SimpleWidget />
</layout>
}
}
export default Crap;
![](https://s3.amazonaws.com/media-p.slid.es/uploads/52782/images/2916162/mobx.png)
@observer
![](https://s3.amazonaws.com/media-p.slid.es/uploads/52782/images/2916162/mobx.png)
@observer
makes render() autorun
![](https://s3.amazonaws.com/media-p.slid.es/uploads/52782/images/2916162/mobx.png)
@observer
makes render() autorun
adds "pure render" mixin
class Counter extends React.Component {
@observable times = 0
render() {
var addOne = ()=> this.times++;
return <div>
<label>clicked {this.times} times</label>
<button onClick={addOne}>+1</button>
</div>
}
}
![](https://s3.amazonaws.com/media-p.slid.es/uploads/52782/images/2916162/mobx.png)
Local State
class Counter extends React.Component {
@observable times = 0
render() {
var addOne = ()=> this.times++;
return <div>
<label>clicked {this.times} times</label>
<button onClick={addOne}>+1</button>
</div>
}
}
![](https://s3.amazonaws.com/media-p.slid.es/uploads/52782/images/2916162/mobx.png)
Local State
Questions?
MobX
By Vitali Perchonok
MobX
- 1,548