SlideShare a Scribd company logo
1
ReduxRedux
Raise your hand if you used in production...Raise your hand if you used in production...
2
MobXMobX
Raise your hand if you used in production...Raise your hand if you used in production...
3
React.createContextReact.createContext
Do you want to experiment with justDo you want to experiment with just
I hope I'll change your idea!
I hope I'll change your idea!
4
Ego SlideEgo Slide
@mattiamanzati
5
What does anWhat does an
application needs?application needs?
6
What does anWhat does an
application needs?application needs?
STORE ITS STATE
6
What does anWhat does an
application needs?application needs?
STORE ITS STATEMODIFY ITS STATE
6
What does anWhat does an
application needs?application needs?
STORE ITS STATEMODIFY ITS STATE
VIEW ITS STATE
AGGREGATED
6
What does anWhat does an
application needs?application needs?
STORE ITS STATEMODIFY ITS STATE
VIEW ITS STATE
AGGREGATED
PERFORM
SIDE EFFECTS
6
...JavaScript?...JavaScript?
7
...JavaScript?...JavaScript?
{ 
name: "work", 
done: false 
}
7
...JavaScript?...JavaScript?
{ 
name: "work", 
done: false 
}
onClick = () =>{ 
todo.done = true 
}
7
...JavaScript?...JavaScript?
{ 
name: "work", 
done: false 
}
onClick = () =>{ 
todo.done = true 
}
get totalCount(){ 
return this.todos.length 
}
7
...JavaScript?...JavaScript?
{ 
name: "work", 
done: false 
}
onClick = () =>{ 
todo.done = true 
}
get totalCount(){ 
return this.todos.length 
}
???
7
MobXMobX
8
MobXMobX
Reactive library
8
MobXMobX
Reactive library
No concept of stream
8
MobXMobX
Reactive library
No concept of stream
Uses Proxies (ES6+) or Atoms (ES5-)
8
ObservablesObservables
ActionsActions
9
ObservablesObservables
Store your application state
ActionsActions
9
ObservablesObservables
Store your application state
State may change over time
ActionsActions
9
ObservablesObservables
Store your application state
State may change over time
Observable instance may be the same
ActionsActions
9
ObservablesObservables
Store your application state
State may change over time
Observable instance may be the same
ActionsActions
Change observables state value
9
ObservablesObservables
Store your application state
State may change over time
Observable instance may be the same
ActionsActions
Change observables state value
Can be triggered from UI or side effects
9
ComputedsComputeds
ReactionsReactions
10
ComputedsComputeds
Derived state data
ReactionsReactions
10
ComputedsComputeds
Derived state data
Automatically updated synchronusly
ReactionsReactions
10
ComputedsComputeds
Derived state data
Automatically updated synchronusly
Always up to date with current
observable state
ReactionsReactions
10
ComputedsComputeds
Derived state data
Automatically updated synchronusly
Always up to date with current
observable state
ReactionsReactions
Trigger functions when a condition changes
10
ComputedsComputeds
Derived state data
Automatically updated synchronusly
Always up to date with current
observable state
ReactionsReactions
Trigger functions when a condition changes
UI is a reaction of the store state
10
A successful pattern inA successful pattern in
history!history!
11
12
Excel is Reactive!Excel is Reactive!
13
Excel is Reactive!Excel is Reactive!
CELLS
13
Excel is Reactive!Excel is Reactive!
CELLSUSER INTERACTION
13
Excel is Reactive!Excel is Reactive!
CELLSUSER INTERACTION
FORMULAS
13
Excel is Reactive!Excel is Reactive!
CELLSUSER INTERACTION
FORMULAS
SCREEN UPDATES
13
Let's start!Let's start!
A classic example: TODO ListsA classic example: TODO Lists
14
class App extends React.Component {
// observable values
currentText = "";
todos = [];
// actions
addTodo = () => {
this.todos.push({ name: this.currentText, done: false });
this.currentText = "";
};
toggleTodo = todo => {
todo.done = !todo.done;
};
setCurrentText = text => {
this.currentText = text;
};
// computed
get pendingCount() {
return this.todos.filter(todo => !todo.done).length;
}
// ...
} 15
class App extends React.Component {
// observable values
currentText = "";
todos = [];
// actions
addTodo = () => {
this.todos.push({ name: this.currentText, done: false });
this.currentText = "";
};
toggleTodo = todo => {
todo.done = !todo.done;
};
setCurrentText = text => {
this.currentText = text;
};
// computed
get pendingCount() {
return this.todos.filter(todo => !todo.done).length;
}
// ...
} 15
class App extends React.Component {
// observable values
currentText = "";
todos = [];
// actions
addTodo = () => {
this.todos.push({ name: this.currentText, done: false });
this.currentText = "";
};
toggleTodo = todo => {
todo.done = !todo.done;
};
setCurrentText = text => {
this.currentText = text;
};
// computed
get pendingCount() {
return this.todos.filter(todo => !todo.done).length;
}
// ...
} 15
class App extends React.Component {
// observable values
currentText = "";
todos = [];
// actions
addTodo = () => {
this.todos.push({ name: this.currentText, done: false });
this.currentText = "";
};
toggleTodo = todo => {
todo.done = !todo.done;
};
setCurrentText = text => {
this.currentText = text;
};
// computed
get pendingCount() {
return this.todos.filter(todo => !todo.done).length;
}
// ...
} 15
class App extends React.Component {
// ...
// render observer
render() {
return (
<div className="App">
<h1>TODOs</h1>
<input
type="text"
value={this.currentText}
onChange={e => this.setCurrentText(e.target.value)}
/>
<button onClick={this.addTodo}>Add</button>
<ul>
{this.todos.map(item => (
<li onClick={() => this.toggleTodo(item)}>
{item.name} {item.done ? <i>DONE!</i> : null}
</li>
))}
</ul>
<p>There are {this.pendingCount} pending todos.</p>
</div>
);
16
class App extends React.Component {
// ...
// render observer
render() {
return (
<div className="App">
<h1>TODOs</h1>
<input
type="text"
value={this.currentText}
onChange={e => this.setCurrentText(e.target.value)}
/>
<button onClick={this.addTodo}>Add</button>
<ul>
{this.todos.map(item => (
<li onClick={() => this.toggleTodo(item)}>
{item.name} {item.done ? <i>DONE!</i> : null}
</li>
))}
</ul>
<p>There are {this.pendingCount} pending todos.</p>
</div>
);
16
class App extends React.Component {
// ...
}
const MyApp = observer(App);
decorate(App, {
currentText: observable,
todos: observable,
addTodo: action,
toggleTodo: action,
setCurrentText: action,
pendingCount: computed
});
const rootElement = document.getElementById("root");
ReactDOM.render(<MyApp />, rootElement);
17
class App extends React.Component {
// ...
}
const MyApp = observer(App);
decorate(App, {
currentText: observable,
todos: observable,
addTodo: action,
toggleTodo: action,
setCurrentText: action,
pendingCount: computed
});
const rootElement = document.getElementById("root");
ReactDOM.render(<MyApp />, rootElement);
17
Few notes:Few notes:
Component state managed byComponent state managed by
MobX or not?MobX or not?
class App extends React.Component {
@observable
currentText = ""
// ...
}
export default observer(App)
class App extends React.Component {
state = {
currentText: ""
}
// ...
} 18
Few notes:Few notes:
Component state managed byComponent state managed by
MobX or not?MobX or not?
class App extends React.Component {
@observable
currentText = ""
// ...
}
export default observer(App)
19
Few notes:Few notes:
Component state managed byComponent state managed by
MobX or not?MobX or not?
Better optimized than setState
class App extends React.Component {
@observable
currentText = ""
// ...
}
export default observer(App)
19
Few notes:Few notes:
Component state managed byComponent state managed by
MobX or not?MobX or not?
Better optimized than setState
Simpler API than setState
class App extends React.Component {
@observable
currentText = ""
// ...
}
export default observer(App)
19
Few notes:Few notes:
Component state managed byComponent state managed by
MobX or not?MobX or not?
Better optimized than setState
Simpler API than setState
Easier to refactor to a separate store
class App extends React.Component {
@observable
currentText = ""
// ...
}
export default observer(App)
19
Few notes:Few notes:
Decorators are optionalDecorators are optional
class Store {
@observable
todos = []
}
class Store {
todos = []
}
decorate(Store, {
todos: observable
});
20
Few notes:Few notes:
Classes are optionalClasses are optional
class Store {
@observable
todos = []
}
const store = new Store()
const store = observable({
todos: []
})
21
MobX is unopinionatedMobX is unopinionated
22
MobX is unopinionatedMobX is unopinionated
How do I X?
22
MobX is unopinionatedMobX is unopinionated
How do I X?
Should I MVC?
22
MobX is unopinionatedMobX is unopinionated
How do I X?
Should I MVC?
Should I MVVM?
22
MobX is unopinionatedMobX is unopinionated
How do I X?
Should I MVC?
Should I MVVM?
Should I MVP?
22
MobX is unopinionatedMobX is unopinionated
How do I X?
Should I MVC?
Should I MVVM?
Should I MVP?
Should I Flux?
22
MobX is aMobX is a
reactive data libraryreactive data library
23
It works!It works!
24
It works!It works!
Does it scales?
Does it scales?
24
It works!It works!
Is it testable?
Is it testable?
Does it scales?
Does it scales?
24
It works!It works!
Is it testable?
Is it testable?
Mix BL and UI?
Mix BL and UI?
Does it scales?
Does it scales?
24
Don't mix Business Logic & UIDon't mix Business Logic & UI
25
Don't mix Business Logic & UIDon't mix Business Logic & UI
What if we want a React Native app later?
25
Don't mix Business Logic & UIDon't mix Business Logic & UI
What if we want a React Native app later?
What if UI framework changes?
25
Don't mix Business Logic & UIDon't mix Business Logic & UI
What if we want a React Native app later?
What if UI framework changes?
What if we need SSR?
25
Don't mix Business Logic & UIDon't mix Business Logic & UI
What if we want a React Native app later?
What if UI framework changes?
What if we need SSR?
25
Don't mix Business Logic & UIDon't mix Business Logic & UI
class Store {
// observable values
currentText = "";
todos = [];
// actions
addTodo = () => {
this.todos.push({ name: this.currentText, done: false });
this.currentText = "";
};
toggleTodo = todo => { todo.done = !todo.done; };
setCurrentText = text => { this.currentText = text; };
// computed
get pendingCount() {
return this.todos.filter(todo => !todo.done).length;
}
}
Extracting the StoreExtracting the Store
26
class Store {
// ...
}
class App extends React.Component {
// ...
}
const MyApp = observer(Store);
const store = new Store();
const rootElement = document.getElementById("root");
ReactDOM.render(<MyApp store={store} />, rootElement);
Don't mix Business Logic & UIDon't mix Business Logic & UI
Wiring the Store as propWiring the Store as prop
27
import { observer, Provider, inject }
from "mobx-react";
// ...
const MyApp = inject("store")(observer(App));
const store = new Store();
const rootElement = document.getElementById("root");
ReactDOM.render(
<Provider store={store}>
<MyApp />
</Provider>, rootElement);
Don't mix Business Logic & UIDon't mix Business Logic & UI
Use Provider & InjectUse Provider & Inject
28
import { observer, Provider, inject }
from "mobx-react";
// ...
const MyApp = inject("store")(observer(App));
const store = new Store();
const rootElement = document.getElementById("root");
ReactDOM.render(
<Provider store={store}>
<MyApp />
</Provider>, rootElement);
Don't mix Business Logic & UIDon't mix Business Logic & UI
Use Provider & InjectUse Provider & Inject
No need to pass down manually your stores
28
import { observer, Provider, inject }
from "mobx-react";
// ...
const MyApp = inject("store")(observer(App));
const store = new Store();
const rootElement = document.getElementById("root");
ReactDOM.render(
<Provider store={store}>
<MyApp />
</Provider>, rootElement);
Don't mix Business Logic & UIDon't mix Business Logic & UI
Use Provider & InjectUse Provider & Inject
No need to pass down manually your stores
Allows to change store implementation in your tests
28
import { observer, Provider, inject }
from "mobx-react";
// ...
const MyApp = inject("store")(observer(App));
const store = new Store();
const rootElement = document.getElementById("root");
ReactDOM.render(
<Provider store={store}>
<MyApp />
</Provider>, rootElement);
Don't mix Business Logic & UIDon't mix Business Logic & UI
Use Provider & InjectUse Provider & Inject
No need to pass down manually your stores
Allows to change store implementation in your tests
Only one point of store injections into the views
28
Real-World ArchitectureReal-World Architecture
Current State RecapCurrent State Recap
VIEW
STORE
Renders the view and
calls actions on the store
Holds and modify domain state
Holds and modify application state
Implements business logic
Fetch from API
Implements view actions
Aggregates data for the view
29
Real-World ArchitectureReal-World Architecture
Current State RecapCurrent State Recap
VIEW
STORE
Renders the view and
calls actions on the store
Holds and modify domain state
Holds and modify application state
Implements business logic
Fetch from API
Implements view actions
Aggregates data for the view
W
ay too m
uch
W
ay too m
uch
things!
things!
29
Domain StateDomain State
WTF is dat?WTF is dat?
30
Domain StateDomain State
WTF is dat?WTF is dat?
Is the domain of your app
in our example, of Todo
30
Domain StateDomain State
WTF is dat?WTF is dat?
Is the domain of your app
in our example, of Todo
Describes the application entities and relations
30
Domain StateDomain State
WTF is dat?WTF is dat?
Is the domain of your app
in our example, of Todo
Describes the application entities and relations
Usually it is persistent and stored somewhere
30
Domain StateDomain State
WTF is dat?WTF is dat?
Is the domain of your app
in our example, of Todo
Describes the application entities and relations
Usually it is persistent and stored somewhere
Not tight with the UI
30
Domain StateDomain State
WTF is dat?WTF is dat?
Is the domain of your app
in our example, of Todo
Describes the application entities and relations
Usually it is persistent and stored somewhere
Not tight with the UI
Highly reusable
30
Domain StateDomain State
first implementationfirst implementation
class Todo {
name = "";
done = false;
}
decorate(Todo, {
name: observable,
done: observable
});
class Store {
// ...
addTodo = () => {
const todo = new Todo();
todo.name = this.currentText;
this.todos.push(todo);
this.currentText = "";
};
}
31
Domain StateDomain State
domain actionsdomain actions
// domain todo model
class Todo {
name = "";
done = false;
toggle(){
this.done = !this.done
}
}
decorate(Todo, {
// ...
toggle: action
});
Observables should be changed trough actions! So it's a
good idea to define dumb actions on our domain model!
32
Domain StateDomain State
let's be more real!let's be more real!
// domain todo model
class Todo {
name = "";
done = false;
user_id = 0; // UHM... is this ok?
// ...
}
// domain user model
class User {
id = 0
name = ""
}
33
Domain StateDomain State
tree or graph?tree or graph?
// domain todo model
class Todo {
name = "";
done = false;
user_id = 0;
// ...
}
// domain user model
class User {
id = 0
name = ""
}
// domain todo model
class Todo {
name = "";
done = false;
user = new User(0, "");
// ...
}
// domain user model
class User {
id = 0
name = ""
}
34
Domain StateDomain State
tree or graph?tree or graph?
// domain todo model
class Todo {
name = "";
done = false;
user_id = 0;
// ...
}
// domain user model
class User {
id = 0
name = ""
}
// domain todo model
class Todo {
name = "";
done = false;
user = new User(0, "");
// ...
}
// domain user model
class User {
id = 0
name = ""
}
Normalized
34
Domain StateDomain State
tree or graph?tree or graph?
// domain todo model
class Todo {
name = "";
done = false;
user_id = 0;
// ...
}
// domain user model
class User {
id = 0
name = ""
}
// domain todo model
class Todo {
name = "";
done = false;
user = new User(0, "");
// ...
}
// domain user model
class User {
id = 0
name = ""
}
Normalized
Object tree
34
Domain StateDomain State
tree or graph?tree or graph?
// domain todo model
class Todo {
name = "";
done = false;
user_id = 0;
// ...
}
// domain user model
class User {
id = 0
name = ""
}
// domain todo model
class Todo {
name = "";
done = false;
user = new User(0, "");
// ...
}
// domain user model
class User {
id = 0
name = ""
}
Normalized
Object tree
Easily serialize
34
Domain StateDomain State
tree or graph?tree or graph?
// domain todo model
class Todo {
name = "";
done = false;
user_id = 0;
// ...
}
// domain user model
class User {
id = 0
name = ""
}
// domain todo model
class Todo {
name = "";
done = false;
user = new User(0, "");
// ...
}
// domain user model
class User {
id = 0
name = ""
}
Normalized
Object tree
Easily serialize
Difficult access 34
Domain StateDomain State
tree or graph?tree or graph?
// domain todo model
class Todo {
name = "";
done = false;
user_id = 0;
// ...
}
// domain user model
class User {
id = 0
name = ""
}
// domain todo model
class Todo {
name = "";
done = false;
user = new User(0, "");
// ...
}
// domain user model
class User {
id = 0
name = ""
}
Normalized
Object tree
Easily serialize
Difficult access
Denormalized
34
Domain StateDomain State
tree or graph?tree or graph?
// domain todo model
class Todo {
name = "";
done = false;
user_id = 0;
// ...
}
// domain user model
class User {
id = 0
name = ""
}
// domain todo model
class Todo {
name = "";
done = false;
user = new User(0, "");
// ...
}
// domain user model
class User {
id = 0
name = ""
}
Normalized
Object tree
Easily serialize
Difficult access
Denormalized
Object graph
34
Domain StateDomain State
tree or graph?tree or graph?
// domain todo model
class Todo {
name = "";
done = false;
user_id = 0;
// ...
}
// domain user model
class User {
id = 0
name = ""
}
// domain todo model
class Todo {
name = "";
done = false;
user = new User(0, "");
// ...
}
// domain user model
class User {
id = 0
name = ""
}
Normalized
Object tree
Easily serialize
Difficult access
Denormalized
Object graph
Harder serialization
34
Domain StateDomain State
tree or graph?tree or graph?
// domain todo model
class Todo {
name = "";
done = false;
user_id = 0;
// ...
}
// domain user model
class User {
id = 0
name = ""
}
// domain todo model
class Todo {
name = "";
done = false;
user = new User(0, "");
// ...
}
// domain user model
class User {
id = 0
name = ""
}
Normalized
Object tree
Easily serialize
Difficult access
Denormalized
Object graph
Harder serialization
Easy access 34
Domain StateDomain State
good news everyone!good news everyone!
35
Domain StateDomain State
lets do both!lets do both!
// domain todo model
class Todo {
name = ""
done = false
user_id = 0
get user(){
return store.getUserById(this.user_id) // <- WTF
}
set user(value){
this.user_id = value.id
}
}
decorate(Todo, {
//...
user_id: observable,
user: computed
}) 36
Domain StateDomain State
lets do both!lets do both!
// domain todo model
class Todo {
name = ""
done = false
user_id = 0
get user(){
return store.getUserById(this.user_id) // <- WTF
}
set user(value){
this.user_id = value.id
}
}
decorate(Todo, {
//...
user_id: observable,
user: computed
}) 36
Multiple Store CommunicationMultiple Store Communication
available pattersavailable patters
37
Multiple Store CommunicationMultiple Store Communication
available pattersavailable patters
Singleton instance & require/import
37
Multiple Store CommunicationMultiple Store Communication
available pattersavailable patters
Singleton instance & require/import
Dependency injection framework
37
Multiple Store CommunicationMultiple Store Communication
available pattersavailable patters
Singleton instance & require/import
Dependency injection framework
Root store pattern
37
Multiple Store CommunicationMultiple Store Communication
root store patternroot store pattern
class RootStore {
todoStore = null;
fetch = null;
apiKey = "";
constructor(fetch, apiKey){
this.fetch = fetch
this.apiKey = apiKey
this.todoStore = new Store(this)
}
}
class Todo {
store = null;
constructor(store){ this.store = store }
}
class Store {
rootStore = null;
constructor(rootStore){ this.rootStore = rootStore }
} 38
Multiple Store CommunicationMultiple Store Communication
root store patternroot store pattern
39
Multiple Store CommunicationMultiple Store Communication
root store patternroot store pattern
Central point for each store to
communicate
39
Multiple Store CommunicationMultiple Store Communication
root store patternroot store pattern
Central point for each store to
communicate
Strongly typed
39
Multiple Store CommunicationMultiple Store Communication
root store patternroot store pattern
Central point for each store to
communicate
Strongly typed
Works as dependency root
Can host environment specific variables
39
Multiple Store CommunicationMultiple Store Communication
root store patternroot store pattern
Central point for each store to
communicate
Strongly typed
Works as dependency root
Can host environment specific variables
Very easily testable
39
Multiple Store CommunicationMultiple Store Communication
back to our problemback to our problem
// domain todo model
class Todo {
name = ""
done = false
user_id = 0
store = null;
constructor(store){ this.store = store; }
get user(){
return this.store.rootStore
.userStore.getUserById(this.user_id)
}
set user(value){
this.user_id = value.id
}
}
40
Multiple Store CommunicationMultiple Store Communication
back to our problemback to our problem
import { observer, Provider,
inject } from "mobx-react";
// ...
const MyApp = inject("store")(observer(App));
const store = new RootStore(window.fetch);
const rootElement = document.getElementById("root");
ReactDOM.render(
<Provider store={store}>
<MyApp />
</Provider>, rootElement);
41
Multiple Store CommunicationMultiple Store Communication
back to our problemback to our problem
test("it should restore data from API", async t => {
const fakeFetch = () => Promise.resolve({
data: [{id: 1, name: "Mattia"}]
})
const store = new RootStore(fakeFetch)
await store.userStore.fetchAll()
t.equal(store.userStore.users[0].name, "Mattia")
})
42
Real-World ArchitectureReal-World Architecture
Current State RecapCurrent State Recap
VIEW
STORE
Renders the view and
calls actions on the store
Holds domain state
Create and modify domain state
Holds and modify application state
Implements business logic
Fetch from API
Aggregates data for the view
DOMAIN MODEL
43
Real-World ArchitectureReal-World Architecture
Current State RecapCurrent State Recap
VIEW
STORE
Renders the view and
calls actions on the store
Holds domain state
Create and modify domain state
Holds and modify application state
Implements business logic
Fetch from API
Aggregates data for the view
DOMAIN MODEL
43
Domain Model SerializationDomain Model Serialization
turning JSON into observablesturning JSON into observables
44
Domain Model SerializationDomain Model Serialization
turning JSON into observablesturning JSON into observables
Our state is an object tree
44
Domain Model SerializationDomain Model Serialization
turning JSON into observablesturning JSON into observables
Our state is an object tree
Unfortunately it's not a plain JS object
44
Domain Model SerializationDomain Model Serialization
turning JSON into observablesturning JSON into observables
Our state is an object tree
Unfortunately it's not a plain JS object
Most APIs provide JSON as intermediate language
44
Domain Model SerializationDomain Model Serialization
turning JSON into observablesturning JSON into observables
Our state is an object tree
Unfortunately it's not a plain JS object
Most APIs provide JSON as intermediate language
We need to provide conversions to serialize or
deserialize our state
44
Domain Model SerializationDomain Model Serialization
deserializationdeserialization
class Todo {
// ...
constructor(store, data){
this.store = store
if(data) Object.assign(this, data)
}
}
45
Domain Model SerializationDomain Model Serialization
serializationserialization
class Todo {
// ...
get toJSON(){
return {
name: this.name,
done: this.done
}
}
}
// ...
decorate(Todo, {
toJSON: computed
})
JSON is just another view of the domain model,
so we can just derive itderive
46
Domain Model SerializationDomain Model Serialization
packagespackages
class User {
@serializable(identifier()) id = 0;
@serializable name = '';
}
class Todo {
@serializable name = '';
@serializable done = false;
@serializable(object(User)) user = null;
}
// You can now deserialize and serialize!
const todo = deserialize(Todo, {
name: 'Hello world',
done: true,
user: { id: 1, name: 'Mattia' }
});
const todoJSON = serialize(todo)
with serializr you get that with tagging your domain models
47
Domain Model SerializationDomain Model Serialization
the deserialization problemthe deserialization problem
class TodoStore {
todos = []
fromCache(){
const cachedData = localStorage.getItem("todos")
|| "[]"
this.todos = JSON.parse(cachedData)
.map(data => new Todo(this, data)
}
getById = id => this.todos
.find(item => item.id === id)
}
decorate(TodoStore, {
fromCache: action
})
deserializing is memory intensive!
48
MobX hidden featureMobX hidden feature
_interceptReads_interceptReads
import {_interceptReads} from "mobx"
const todos = observable.map()
_interceptReads(todos, value => value + "! LOL")
todos.set(1, "Mattia")
console.log(todos.get("1")) // => Mattia! LOL
undocumented (yet there since 2017) mobx feature
allows to transform mobx values while reading
available for objects, arrays, maps and boxed values
49
MobX hidden featureMobX hidden feature
_interceptReads to the rescue!_interceptReads to the rescue!
class TodoStore {
todos = []
_cache = {}
constructor(rootStore){
this.rootStore = rootStore
// ...
_interceptReads(this.todos, this.unboxTodo)
}
unboxTodo = data => {
if(this._cache[data.id]){
return this._cache[data.id]
}
this._cache[data.id] = new Todo(this, data)
return this._cache[data.id]
}
}
50
MobX PerformanceMobX Performance
better performant domain storesbetter performant domain stores
51
MobX PerformanceMobX Performance
better performant domain storesbetter performant domain stores
Use ES6 Maps and lookup by ID when possible
51
MobX PerformanceMobX Performance
better performant domain storesbetter performant domain stores
Use ES6 Maps and lookup by ID when possible
Store JSON in an observable map
51
MobX PerformanceMobX Performance
better performant domain storesbetter performant domain stores
Use ES6 Maps and lookup by ID when possible
Store JSON in an observable map
Use _interceptReads to perform lazy deserialization
51
MobX PerformanceMobX Performance
better performant domain storesbetter performant domain stores
Use ES6 Maps and lookup by ID when possible
Store JSON in an observable map
Use _interceptReads to perform lazy deserialization
Implement ID -> name without deserialization
51
Real-World ArchitectureReal-World Architecture
Current State RecapCurrent State Recap
VIEW
REPOSITORY
Renders the view and
calls actions on the store
Holds domain state
Create and modify domain state
Fetch from API
Holds and modify application state
Implements business logic
Aggregates data for the view
STORE
DOMAIN MODEL
52
Real-World ArchitectureReal-World Architecture
Current State RecapCurrent State Recap
VIEW
REPOSITORY
Renders the view and
calls actions on the store
Holds domain state
Create and modify domain state
Fetch from API
Holds and modify application state
Implements business logic
Aggregates data for the view
STORE
DOMAIN MODEL
52
class Overview {
rootStore = null
currentText = ""
constructor(rootStore){ this.rootStore = rootStore }
get todos(){
// perform sorting and pagining here
return this.rootStore.todoStore.todos
}
handleAddClicked(){
this.rootStore.todoStore.addTodo(this.currentText)
this.currentText = ""
}
}
decorate(Router, {
todos: computed,
currentText: observable,
handleAddClicked: action
})
53
Services & PresentersServices & Presenters
routingrouting
class Router {
uri = "/"
page = null
// ...
constructor(rootStore){
reaction(() => this.uri, uri => this.parseUri(uri))
}
openOverview(){
this.page = { name: "overview", data: { project_id: 1 }}
}
}
decorate(Router, {
uri: observable,
page: observable
})
54
Services & PresentersServices & Presenters
routingrouting
class Router {
uri = "/"
page = null
// ...
constructor(rootStore){
reaction(() => this.uri, uri => this.parseUri(uri))
}
openOverview(){
this.page = { name: "overview", data: { project_id: 1 }}
}
}
decorate(Router, {
uri: observable,
page: observable
}) Where load that data?
Where load that data?
54
Services & PresentersServices & Presenters
view presenterview presenter
class Router {
uri = "/"
page = null
constructor(rootStore){
this.page = this.loadHome()
reaction(() => this.uri, uri => this.parseUri(uri))
}
openOverview(){
this.page = fromPromise(
this.overviewStore.fetchData()
.then(data =>
Promise.resolve({ name: "overview", data })
)
)
}
}
55
Services & PresentersServices & Presenters
view presenterview presenter
class Router {
uri = "/"
page = null
constructor(rootStore){
this.page = this.loadHome()
reaction(() => this.uri, uri => this.parseUri(uri))
}
openOverview(){
this.page = fromPromise(
this.overviewStore.fetchData()
.then(data =>
Promise.resolve({ name: "overview", data })
)
)
}
}
55
Services & PresentersServices & Presenters
fromPromisefromPromise
class Router {
uri = "/"
page = null
constructor(rootStore){
this.page = this.loadHome()
reaction(() => this.uri, uri => this.parseUri(uri))
}
openOverview(){
this.page = fromPromise(
this.overviewStore.fetchData()
.then(data =>
Promise.resolve({ name: "overview", data })
)
)
}
}
56
MobX Async TipsMobX Async Tips
reactive finite state machinesreactive finite state machines
<div>
{ this.page.case({
pending: () => "loading",
rejected: (e) => "error: " + e,
fulfilled: ({name, data}) => {
switch(name){
case "home": return <HomePage data={data} />;
case "overview": return <Overview data={data} />;
}
}
})}
</div>
57
Real-World ArchitectureReal-World Architecture
Current State RecapCurrent State Recap
VIEW
REPOSITORY
Renders the view and
calls actions on the store
Holds domain state
Create and modify domain state
Fetch from API
Holds and modify application state
Implements business logic
SERVICE
DOMAIN MODEL
PRESENTER
Aggregates data for the view
Call actions on the services
58
Today's RecapToday's Recap
59
Today's RecapToday's Recap
1. Use Redux if you're paid by the hour
59
Today's RecapToday's Recap
1. Use Redux if you're paid by the hour
2. Find your minimal state
59
Today's RecapToday's Recap
1. Use Redux if you're paid by the hour
2. Find your minimal state
3. Derive whatever is possible
59
Today's RecapToday's Recap
1. Use Redux if you're paid by the hour
2. Find your minimal state
3. Derive whatever is possible
4. Think as you're building for any UI environment
59
Today's RecapToday's Recap
1. Use Redux if you're paid by the hour
2. Find your minimal state
3. Derive whatever is possible
4. Think as you're building for any UI environment
5. Always experiment and have fun!
59
Want something opinionated?Want something opinionated?
1. Define store shape
2. Automatic serialization/deserialization
3. Implements both a immutable & mutable store
4. Defines a stricter architecture
5. Time travelling
discover mobx-state-treediscover mobx-state-tree
60
Not into mutable things?Not into mutable things?
stay here for the next talk!stay here for the next talk!
61
Thanks for your time!Thanks for your time!
Questions?Questions?
 
@MattiaManzati - slides.com/mattiamanzati
62
ReferencesReferences
 
https://slides.com/mattiamanzati/real-world-
mobx
http://mobx-patterns.surge.sh/
https://medium.com/@mweststrate/ui-as-an-
afterthought-26e5d2bb24d6
https://github.com/mobxjs/mobx
https://github.com/mobxjs/mobx-react
https://github.com/mobxjs/mobx-state-tree
63

More Related Content

PDF
JavaScript Best Pratices
PDF
You do not need automation engineer - Sqa Days - 2015 - EN
PDF
The evolution of asynchronous javascript
PDF
Even more java script best practices
PPTX
Backbone.js and friends
PDF
React state management with Redux and MobX
PDF
Making react part of something greater
PDF
Mob x in react
JavaScript Best Pratices
You do not need automation engineer - Sqa Days - 2015 - EN
The evolution of asynchronous javascript
Even more java script best practices
Backbone.js and friends
React state management with Redux and MobX
Making react part of something greater
Mob x in react

Similar to Mattia Manzati - Real-World MobX Project Architecture - Codemotion Rome 2019 (20)

PDF
Reduxing UI: Borrowing the Best of Web to Make Android Better
PDF
React and MobX: A Truly Reactive App
PDF
React lecture
PDF
React JS and why it's awesome
PDF
Григорий Шехет "Treasure hunt in the land of Reactive frameworks"
PDF
Reactive Programming - ReactFoo 2020 - Aziz Khambati
PDF
Stay with React.js in 2020
PDF
Simple React Todo List
PDF
Redux essentials
PDF
A React Journey
PDF
MobX for dummies
PDF
Introduction to Redux (for Angular and React devs)
PPTX
Reactive programming with rx java
PDF
React HOCs, Context and Observables
PDF
Mobx for Dummies - Yauheni Nikanowich - React Warsaw #5
PDF
Let's discover React and Redux with TypeScript
PDF
Mobx Internals
PDF
MobX: the way to simplicity
PPTX
React + Flux = Joy
PDF
Building React Applications with Redux
Reduxing UI: Borrowing the Best of Web to Make Android Better
React and MobX: A Truly Reactive App
React lecture
React JS and why it's awesome
Григорий Шехет "Treasure hunt in the land of Reactive frameworks"
Reactive Programming - ReactFoo 2020 - Aziz Khambati
Stay with React.js in 2020
Simple React Todo List
Redux essentials
A React Journey
MobX for dummies
Introduction to Redux (for Angular and React devs)
Reactive programming with rx java
React HOCs, Context and Observables
Mobx for Dummies - Yauheni Nikanowich - React Warsaw #5
Let's discover React and Redux with TypeScript
Mobx Internals
MobX: the way to simplicity
React + Flux = Joy
Building React Applications with Redux
Ad

More from Codemotion (20)

PDF
Fuzz-testing: A hacker's approach to making your code more secure | Pascal Ze...
PDF
Pompili - From hero to_zero: The FatalNoise neverending story
PPTX
Pastore - Commodore 65 - La storia
PPTX
Pennisi - Essere Richard Altwasser
PPTX
Michel Schudel - Let's build a blockchain... in 40 minutes! - Codemotion Amst...
PPTX
Richard Süselbeck - Building your own ride share app - Codemotion Amsterdam 2019
PPTX
Eward Driehuis - What we learned from 20.000 attacks - Codemotion Amsterdam 2019
PPTX
Francesco Baldassarri - Deliver Data at Scale - Codemotion Amsterdam 2019 -
PDF
Martin Förtsch, Thomas Endres - Stereoscopic Style Transfer AI - Codemotion A...
PDF
Melanie Rieback, Klaus Kursawe - Blockchain Security: Melting the "Silver Bul...
PDF
Angelo van der Sijpt - How well do you know your network stack? - Codemotion ...
PDF
Lars Wolff - Performance Testing for DevOps in the Cloud - Codemotion Amsterd...
PDF
Sascha Wolter - Conversational AI Demystified - Codemotion Amsterdam 2019
PDF
Michele Tonutti - Scaling is caring - Codemotion Amsterdam 2019
PPTX
Pat Hermens - From 100 to 1,000+ deployments a day - Codemotion Amsterdam 2019
PPTX
James Birnie - Using Many Worlds of Compute Power with Quantum - Codemotion A...
PDF
Don Goodman-Wilson - Chinese food, motor scooters, and open source developmen...
PDF
Pieter Omvlee - The story behind Sketch - Codemotion Amsterdam 2019
PDF
Dave Farley - Taking Back “Software Engineering” - Codemotion Amsterdam 2019
PDF
Joshua Hoffman - Should the CTO be Coding? - Codemotion Amsterdam 2019
Fuzz-testing: A hacker's approach to making your code more secure | Pascal Ze...
Pompili - From hero to_zero: The FatalNoise neverending story
Pastore - Commodore 65 - La storia
Pennisi - Essere Richard Altwasser
Michel Schudel - Let's build a blockchain... in 40 minutes! - Codemotion Amst...
Richard Süselbeck - Building your own ride share app - Codemotion Amsterdam 2019
Eward Driehuis - What we learned from 20.000 attacks - Codemotion Amsterdam 2019
Francesco Baldassarri - Deliver Data at Scale - Codemotion Amsterdam 2019 -
Martin Förtsch, Thomas Endres - Stereoscopic Style Transfer AI - Codemotion A...
Melanie Rieback, Klaus Kursawe - Blockchain Security: Melting the "Silver Bul...
Angelo van der Sijpt - How well do you know your network stack? - Codemotion ...
Lars Wolff - Performance Testing for DevOps in the Cloud - Codemotion Amsterd...
Sascha Wolter - Conversational AI Demystified - Codemotion Amsterdam 2019
Michele Tonutti - Scaling is caring - Codemotion Amsterdam 2019
Pat Hermens - From 100 to 1,000+ deployments a day - Codemotion Amsterdam 2019
James Birnie - Using Many Worlds of Compute Power with Quantum - Codemotion A...
Don Goodman-Wilson - Chinese food, motor scooters, and open source developmen...
Pieter Omvlee - The story behind Sketch - Codemotion Amsterdam 2019
Dave Farley - Taking Back “Software Engineering” - Codemotion Amsterdam 2019
Joshua Hoffman - Should the CTO be Coding? - Codemotion Amsterdam 2019
Ad

Recently uploaded (20)

PDF
Machine learning based COVID-19 study performance prediction
PPTX
A Presentation on Artificial Intelligence
PDF
Chapter 3 Spatial Domain Image Processing.pdf
PPTX
MYSQL Presentation for SQL database connectivity
PPT
“AI and Expert System Decision Support & Business Intelligence Systems”
PPTX
20250228 LYD VKU AI Blended-Learning.pptx
PPTX
KOM of Painting work and Equipment Insulation REV00 update 25-dec.pptx
PDF
Network Security Unit 5.pdf for BCA BBA.
PDF
Blue Purple Modern Animated Computer Science Presentation.pdf.pdf
PPTX
Cloud computing and distributed systems.
PDF
Mobile App Security Testing_ A Comprehensive Guide.pdf
PDF
How UI/UX Design Impacts User Retention in Mobile Apps.pdf
PDF
Agricultural_Statistics_at_a_Glance_2022_0.pdf
PPTX
Understanding_Digital_Forensics_Presentation.pptx
PDF
Architecting across the Boundaries of two Complex Domains - Healthcare & Tech...
PDF
Electronic commerce courselecture one. Pdf
PPTX
Effective Security Operations Center (SOC) A Modern, Strategic, and Threat-In...
PDF
The Rise and Fall of 3GPP – Time for a Sabbatical?
PDF
Diabetes mellitus diagnosis method based random forest with bat algorithm
PPTX
Detection-First SIEM: Rule Types, Dashboards, and Threat-Informed Strategy
Machine learning based COVID-19 study performance prediction
A Presentation on Artificial Intelligence
Chapter 3 Spatial Domain Image Processing.pdf
MYSQL Presentation for SQL database connectivity
“AI and Expert System Decision Support & Business Intelligence Systems”
20250228 LYD VKU AI Blended-Learning.pptx
KOM of Painting work and Equipment Insulation REV00 update 25-dec.pptx
Network Security Unit 5.pdf for BCA BBA.
Blue Purple Modern Animated Computer Science Presentation.pdf.pdf
Cloud computing and distributed systems.
Mobile App Security Testing_ A Comprehensive Guide.pdf
How UI/UX Design Impacts User Retention in Mobile Apps.pdf
Agricultural_Statistics_at_a_Glance_2022_0.pdf
Understanding_Digital_Forensics_Presentation.pptx
Architecting across the Boundaries of two Complex Domains - Healthcare & Tech...
Electronic commerce courselecture one. Pdf
Effective Security Operations Center (SOC) A Modern, Strategic, and Threat-In...
The Rise and Fall of 3GPP – Time for a Sabbatical?
Diabetes mellitus diagnosis method based random forest with bat algorithm
Detection-First SIEM: Rule Types, Dashboards, and Threat-Informed Strategy

Mattia Manzati - Real-World MobX Project Architecture - Codemotion Rome 2019

  • 1. 1
  • 2. ReduxRedux Raise your hand if you used in production...Raise your hand if you used in production... 2
  • 3. MobXMobX Raise your hand if you used in production...Raise your hand if you used in production... 3
  • 4. React.createContextReact.createContext Do you want to experiment with justDo you want to experiment with just I hope I'll change your idea! I hope I'll change your idea! 4
  • 6. What does anWhat does an application needs?application needs? 6
  • 7. What does anWhat does an application needs?application needs? STORE ITS STATE 6
  • 8. What does anWhat does an application needs?application needs? STORE ITS STATEMODIFY ITS STATE 6
  • 9. What does anWhat does an application needs?application needs? STORE ITS STATEMODIFY ITS STATE VIEW ITS STATE AGGREGATED 6
  • 10. What does anWhat does an application needs?application needs? STORE ITS STATEMODIFY ITS STATE VIEW ITS STATE AGGREGATED PERFORM SIDE EFFECTS 6
  • 19. MobXMobX Reactive library No concept of stream Uses Proxies (ES6+) or Atoms (ES5-) 8
  • 22. ObservablesObservables Store your application state State may change over time ActionsActions 9
  • 23. ObservablesObservables Store your application state State may change over time Observable instance may be the same ActionsActions 9
  • 24. ObservablesObservables Store your application state State may change over time Observable instance may be the same ActionsActions Change observables state value 9
  • 25. ObservablesObservables Store your application state State may change over time Observable instance may be the same ActionsActions Change observables state value Can be triggered from UI or side effects 9
  • 28. ComputedsComputeds Derived state data Automatically updated synchronusly ReactionsReactions 10
  • 29. ComputedsComputeds Derived state data Automatically updated synchronusly Always up to date with current observable state ReactionsReactions 10
  • 30. ComputedsComputeds Derived state data Automatically updated synchronusly Always up to date with current observable state ReactionsReactions Trigger functions when a condition changes 10
  • 31. ComputedsComputeds Derived state data Automatically updated synchronusly Always up to date with current observable state ReactionsReactions Trigger functions when a condition changes UI is a reaction of the store state 10
  • 32. A successful pattern inA successful pattern in history!history! 11
  • 33. 12
  • 34. Excel is Reactive!Excel is Reactive! 13
  • 35. Excel is Reactive!Excel is Reactive! CELLS 13
  • 36. Excel is Reactive!Excel is Reactive! CELLSUSER INTERACTION 13
  • 37. Excel is Reactive!Excel is Reactive! CELLSUSER INTERACTION FORMULAS 13
  • 38. Excel is Reactive!Excel is Reactive! CELLSUSER INTERACTION FORMULAS SCREEN UPDATES 13
  • 39. Let's start!Let's start! A classic example: TODO ListsA classic example: TODO Lists 14
  • 40. class App extends React.Component { // observable values currentText = ""; todos = []; // actions addTodo = () => { this.todos.push({ name: this.currentText, done: false }); this.currentText = ""; }; toggleTodo = todo => { todo.done = !todo.done; }; setCurrentText = text => { this.currentText = text; }; // computed get pendingCount() { return this.todos.filter(todo => !todo.done).length; } // ... } 15
  • 41. class App extends React.Component { // observable values currentText = ""; todos = []; // actions addTodo = () => { this.todos.push({ name: this.currentText, done: false }); this.currentText = ""; }; toggleTodo = todo => { todo.done = !todo.done; }; setCurrentText = text => { this.currentText = text; }; // computed get pendingCount() { return this.todos.filter(todo => !todo.done).length; } // ... } 15
  • 42. class App extends React.Component { // observable values currentText = ""; todos = []; // actions addTodo = () => { this.todos.push({ name: this.currentText, done: false }); this.currentText = ""; }; toggleTodo = todo => { todo.done = !todo.done; }; setCurrentText = text => { this.currentText = text; }; // computed get pendingCount() { return this.todos.filter(todo => !todo.done).length; } // ... } 15
  • 43. class App extends React.Component { // observable values currentText = ""; todos = []; // actions addTodo = () => { this.todos.push({ name: this.currentText, done: false }); this.currentText = ""; }; toggleTodo = todo => { todo.done = !todo.done; }; setCurrentText = text => { this.currentText = text; }; // computed get pendingCount() { return this.todos.filter(todo => !todo.done).length; } // ... } 15
  • 44. class App extends React.Component { // ... // render observer render() { return ( <div className="App"> <h1>TODOs</h1> <input type="text" value={this.currentText} onChange={e => this.setCurrentText(e.target.value)} /> <button onClick={this.addTodo}>Add</button> <ul> {this.todos.map(item => ( <li onClick={() => this.toggleTodo(item)}> {item.name} {item.done ? <i>DONE!</i> : null} </li> ))} </ul> <p>There are {this.pendingCount} pending todos.</p> </div> ); 16
  • 45. class App extends React.Component { // ... // render observer render() { return ( <div className="App"> <h1>TODOs</h1> <input type="text" value={this.currentText} onChange={e => this.setCurrentText(e.target.value)} /> <button onClick={this.addTodo}>Add</button> <ul> {this.todos.map(item => ( <li onClick={() => this.toggleTodo(item)}> {item.name} {item.done ? <i>DONE!</i> : null} </li> ))} </ul> <p>There are {this.pendingCount} pending todos.</p> </div> ); 16
  • 46. class App extends React.Component { // ... } const MyApp = observer(App); decorate(App, { currentText: observable, todos: observable, addTodo: action, toggleTodo: action, setCurrentText: action, pendingCount: computed }); const rootElement = document.getElementById("root"); ReactDOM.render(<MyApp />, rootElement); 17
  • 47. class App extends React.Component { // ... } const MyApp = observer(App); decorate(App, { currentText: observable, todos: observable, addTodo: action, toggleTodo: action, setCurrentText: action, pendingCount: computed }); const rootElement = document.getElementById("root"); ReactDOM.render(<MyApp />, rootElement); 17
  • 48. Few notes:Few notes: Component state managed byComponent state managed by MobX or not?MobX or not? class App extends React.Component { @observable currentText = "" // ... } export default observer(App) class App extends React.Component { state = { currentText: "" } // ... } 18
  • 49. Few notes:Few notes: Component state managed byComponent state managed by MobX or not?MobX or not? class App extends React.Component { @observable currentText = "" // ... } export default observer(App) 19
  • 50. Few notes:Few notes: Component state managed byComponent state managed by MobX or not?MobX or not? Better optimized than setState class App extends React.Component { @observable currentText = "" // ... } export default observer(App) 19
  • 51. Few notes:Few notes: Component state managed byComponent state managed by MobX or not?MobX or not? Better optimized than setState Simpler API than setState class App extends React.Component { @observable currentText = "" // ... } export default observer(App) 19
  • 52. Few notes:Few notes: Component state managed byComponent state managed by MobX or not?MobX or not? Better optimized than setState Simpler API than setState Easier to refactor to a separate store class App extends React.Component { @observable currentText = "" // ... } export default observer(App) 19
  • 53. Few notes:Few notes: Decorators are optionalDecorators are optional class Store { @observable todos = [] } class Store { todos = [] } decorate(Store, { todos: observable }); 20
  • 54. Few notes:Few notes: Classes are optionalClasses are optional class Store { @observable todos = [] } const store = new Store() const store = observable({ todos: [] }) 21
  • 55. MobX is unopinionatedMobX is unopinionated 22
  • 56. MobX is unopinionatedMobX is unopinionated How do I X? 22
  • 57. MobX is unopinionatedMobX is unopinionated How do I X? Should I MVC? 22
  • 58. MobX is unopinionatedMobX is unopinionated How do I X? Should I MVC? Should I MVVM? 22
  • 59. MobX is unopinionatedMobX is unopinionated How do I X? Should I MVC? Should I MVVM? Should I MVP? 22
  • 60. MobX is unopinionatedMobX is unopinionated How do I X? Should I MVC? Should I MVVM? Should I MVP? Should I Flux? 22
  • 61. MobX is aMobX is a reactive data libraryreactive data library 23
  • 63. It works!It works! Does it scales? Does it scales? 24
  • 64. It works!It works! Is it testable? Is it testable? Does it scales? Does it scales? 24
  • 65. It works!It works! Is it testable? Is it testable? Mix BL and UI? Mix BL and UI? Does it scales? Does it scales? 24
  • 66. Don't mix Business Logic & UIDon't mix Business Logic & UI 25
  • 67. Don't mix Business Logic & UIDon't mix Business Logic & UI What if we want a React Native app later? 25
  • 68. Don't mix Business Logic & UIDon't mix Business Logic & UI What if we want a React Native app later? What if UI framework changes? 25
  • 69. Don't mix Business Logic & UIDon't mix Business Logic & UI What if we want a React Native app later? What if UI framework changes? What if we need SSR? 25
  • 70. Don't mix Business Logic & UIDon't mix Business Logic & UI What if we want a React Native app later? What if UI framework changes? What if we need SSR? 25
  • 71. Don't mix Business Logic & UIDon't mix Business Logic & UI class Store { // observable values currentText = ""; todos = []; // actions addTodo = () => { this.todos.push({ name: this.currentText, done: false }); this.currentText = ""; }; toggleTodo = todo => { todo.done = !todo.done; }; setCurrentText = text => { this.currentText = text; }; // computed get pendingCount() { return this.todos.filter(todo => !todo.done).length; } } Extracting the StoreExtracting the Store 26
  • 72. class Store { // ... } class App extends React.Component { // ... } const MyApp = observer(Store); const store = new Store(); const rootElement = document.getElementById("root"); ReactDOM.render(<MyApp store={store} />, rootElement); Don't mix Business Logic & UIDon't mix Business Logic & UI Wiring the Store as propWiring the Store as prop 27
  • 73. import { observer, Provider, inject } from "mobx-react"; // ... const MyApp = inject("store")(observer(App)); const store = new Store(); const rootElement = document.getElementById("root"); ReactDOM.render( <Provider store={store}> <MyApp /> </Provider>, rootElement); Don't mix Business Logic & UIDon't mix Business Logic & UI Use Provider & InjectUse Provider & Inject 28
  • 74. import { observer, Provider, inject } from "mobx-react"; // ... const MyApp = inject("store")(observer(App)); const store = new Store(); const rootElement = document.getElementById("root"); ReactDOM.render( <Provider store={store}> <MyApp /> </Provider>, rootElement); Don't mix Business Logic & UIDon't mix Business Logic & UI Use Provider & InjectUse Provider & Inject No need to pass down manually your stores 28
  • 75. import { observer, Provider, inject } from "mobx-react"; // ... const MyApp = inject("store")(observer(App)); const store = new Store(); const rootElement = document.getElementById("root"); ReactDOM.render( <Provider store={store}> <MyApp /> </Provider>, rootElement); Don't mix Business Logic & UIDon't mix Business Logic & UI Use Provider & InjectUse Provider & Inject No need to pass down manually your stores Allows to change store implementation in your tests 28
  • 76. import { observer, Provider, inject } from "mobx-react"; // ... const MyApp = inject("store")(observer(App)); const store = new Store(); const rootElement = document.getElementById("root"); ReactDOM.render( <Provider store={store}> <MyApp /> </Provider>, rootElement); Don't mix Business Logic & UIDon't mix Business Logic & UI Use Provider & InjectUse Provider & Inject No need to pass down manually your stores Allows to change store implementation in your tests Only one point of store injections into the views 28
  • 77. Real-World ArchitectureReal-World Architecture Current State RecapCurrent State Recap VIEW STORE Renders the view and calls actions on the store Holds and modify domain state Holds and modify application state Implements business logic Fetch from API Implements view actions Aggregates data for the view 29
  • 78. Real-World ArchitectureReal-World Architecture Current State RecapCurrent State Recap VIEW STORE Renders the view and calls actions on the store Holds and modify domain state Holds and modify application state Implements business logic Fetch from API Implements view actions Aggregates data for the view W ay too m uch W ay too m uch things! things! 29
  • 79. Domain StateDomain State WTF is dat?WTF is dat? 30
  • 80. Domain StateDomain State WTF is dat?WTF is dat? Is the domain of your app in our example, of Todo 30
  • 81. Domain StateDomain State WTF is dat?WTF is dat? Is the domain of your app in our example, of Todo Describes the application entities and relations 30
  • 82. Domain StateDomain State WTF is dat?WTF is dat? Is the domain of your app in our example, of Todo Describes the application entities and relations Usually it is persistent and stored somewhere 30
  • 83. Domain StateDomain State WTF is dat?WTF is dat? Is the domain of your app in our example, of Todo Describes the application entities and relations Usually it is persistent and stored somewhere Not tight with the UI 30
  • 84. Domain StateDomain State WTF is dat?WTF is dat? Is the domain of your app in our example, of Todo Describes the application entities and relations Usually it is persistent and stored somewhere Not tight with the UI Highly reusable 30
  • 85. Domain StateDomain State first implementationfirst implementation class Todo { name = ""; done = false; } decorate(Todo, { name: observable, done: observable }); class Store { // ... addTodo = () => { const todo = new Todo(); todo.name = this.currentText; this.todos.push(todo); this.currentText = ""; }; } 31
  • 86. Domain StateDomain State domain actionsdomain actions // domain todo model class Todo { name = ""; done = false; toggle(){ this.done = !this.done } } decorate(Todo, { // ... toggle: action }); Observables should be changed trough actions! So it's a good idea to define dumb actions on our domain model! 32
  • 87. Domain StateDomain State let's be more real!let's be more real! // domain todo model class Todo { name = ""; done = false; user_id = 0; // UHM... is this ok? // ... } // domain user model class User { id = 0 name = "" } 33
  • 88. Domain StateDomain State tree or graph?tree or graph? // domain todo model class Todo { name = ""; done = false; user_id = 0; // ... } // domain user model class User { id = 0 name = "" } // domain todo model class Todo { name = ""; done = false; user = new User(0, ""); // ... } // domain user model class User { id = 0 name = "" } 34
  • 89. Domain StateDomain State tree or graph?tree or graph? // domain todo model class Todo { name = ""; done = false; user_id = 0; // ... } // domain user model class User { id = 0 name = "" } // domain todo model class Todo { name = ""; done = false; user = new User(0, ""); // ... } // domain user model class User { id = 0 name = "" } Normalized 34
  • 90. Domain StateDomain State tree or graph?tree or graph? // domain todo model class Todo { name = ""; done = false; user_id = 0; // ... } // domain user model class User { id = 0 name = "" } // domain todo model class Todo { name = ""; done = false; user = new User(0, ""); // ... } // domain user model class User { id = 0 name = "" } Normalized Object tree 34
  • 91. Domain StateDomain State tree or graph?tree or graph? // domain todo model class Todo { name = ""; done = false; user_id = 0; // ... } // domain user model class User { id = 0 name = "" } // domain todo model class Todo { name = ""; done = false; user = new User(0, ""); // ... } // domain user model class User { id = 0 name = "" } Normalized Object tree Easily serialize 34
  • 92. Domain StateDomain State tree or graph?tree or graph? // domain todo model class Todo { name = ""; done = false; user_id = 0; // ... } // domain user model class User { id = 0 name = "" } // domain todo model class Todo { name = ""; done = false; user = new User(0, ""); // ... } // domain user model class User { id = 0 name = "" } Normalized Object tree Easily serialize Difficult access 34
  • 93. Domain StateDomain State tree or graph?tree or graph? // domain todo model class Todo { name = ""; done = false; user_id = 0; // ... } // domain user model class User { id = 0 name = "" } // domain todo model class Todo { name = ""; done = false; user = new User(0, ""); // ... } // domain user model class User { id = 0 name = "" } Normalized Object tree Easily serialize Difficult access Denormalized 34
  • 94. Domain StateDomain State tree or graph?tree or graph? // domain todo model class Todo { name = ""; done = false; user_id = 0; // ... } // domain user model class User { id = 0 name = "" } // domain todo model class Todo { name = ""; done = false; user = new User(0, ""); // ... } // domain user model class User { id = 0 name = "" } Normalized Object tree Easily serialize Difficult access Denormalized Object graph 34
  • 95. Domain StateDomain State tree or graph?tree or graph? // domain todo model class Todo { name = ""; done = false; user_id = 0; // ... } // domain user model class User { id = 0 name = "" } // domain todo model class Todo { name = ""; done = false; user = new User(0, ""); // ... } // domain user model class User { id = 0 name = "" } Normalized Object tree Easily serialize Difficult access Denormalized Object graph Harder serialization 34
  • 96. Domain StateDomain State tree or graph?tree or graph? // domain todo model class Todo { name = ""; done = false; user_id = 0; // ... } // domain user model class User { id = 0 name = "" } // domain todo model class Todo { name = ""; done = false; user = new User(0, ""); // ... } // domain user model class User { id = 0 name = "" } Normalized Object tree Easily serialize Difficult access Denormalized Object graph Harder serialization Easy access 34
  • 97. Domain StateDomain State good news everyone!good news everyone! 35
  • 98. Domain StateDomain State lets do both!lets do both! // domain todo model class Todo { name = "" done = false user_id = 0 get user(){ return store.getUserById(this.user_id) // <- WTF } set user(value){ this.user_id = value.id } } decorate(Todo, { //... user_id: observable, user: computed }) 36
  • 99. Domain StateDomain State lets do both!lets do both! // domain todo model class Todo { name = "" done = false user_id = 0 get user(){ return store.getUserById(this.user_id) // <- WTF } set user(value){ this.user_id = value.id } } decorate(Todo, { //... user_id: observable, user: computed }) 36
  • 100. Multiple Store CommunicationMultiple Store Communication available pattersavailable patters 37
  • 101. Multiple Store CommunicationMultiple Store Communication available pattersavailable patters Singleton instance & require/import 37
  • 102. Multiple Store CommunicationMultiple Store Communication available pattersavailable patters Singleton instance & require/import Dependency injection framework 37
  • 103. Multiple Store CommunicationMultiple Store Communication available pattersavailable patters Singleton instance & require/import Dependency injection framework Root store pattern 37
  • 104. Multiple Store CommunicationMultiple Store Communication root store patternroot store pattern class RootStore { todoStore = null; fetch = null; apiKey = ""; constructor(fetch, apiKey){ this.fetch = fetch this.apiKey = apiKey this.todoStore = new Store(this) } } class Todo { store = null; constructor(store){ this.store = store } } class Store { rootStore = null; constructor(rootStore){ this.rootStore = rootStore } } 38
  • 105. Multiple Store CommunicationMultiple Store Communication root store patternroot store pattern 39
  • 106. Multiple Store CommunicationMultiple Store Communication root store patternroot store pattern Central point for each store to communicate 39
  • 107. Multiple Store CommunicationMultiple Store Communication root store patternroot store pattern Central point for each store to communicate Strongly typed 39
  • 108. Multiple Store CommunicationMultiple Store Communication root store patternroot store pattern Central point for each store to communicate Strongly typed Works as dependency root Can host environment specific variables 39
  • 109. Multiple Store CommunicationMultiple Store Communication root store patternroot store pattern Central point for each store to communicate Strongly typed Works as dependency root Can host environment specific variables Very easily testable 39
  • 110. Multiple Store CommunicationMultiple Store Communication back to our problemback to our problem // domain todo model class Todo { name = "" done = false user_id = 0 store = null; constructor(store){ this.store = store; } get user(){ return this.store.rootStore .userStore.getUserById(this.user_id) } set user(value){ this.user_id = value.id } } 40
  • 111. Multiple Store CommunicationMultiple Store Communication back to our problemback to our problem import { observer, Provider, inject } from "mobx-react"; // ... const MyApp = inject("store")(observer(App)); const store = new RootStore(window.fetch); const rootElement = document.getElementById("root"); ReactDOM.render( <Provider store={store}> <MyApp /> </Provider>, rootElement); 41
  • 112. Multiple Store CommunicationMultiple Store Communication back to our problemback to our problem test("it should restore data from API", async t => { const fakeFetch = () => Promise.resolve({ data: [{id: 1, name: "Mattia"}] }) const store = new RootStore(fakeFetch) await store.userStore.fetchAll() t.equal(store.userStore.users[0].name, "Mattia") }) 42
  • 113. Real-World ArchitectureReal-World Architecture Current State RecapCurrent State Recap VIEW STORE Renders the view and calls actions on the store Holds domain state Create and modify domain state Holds and modify application state Implements business logic Fetch from API Aggregates data for the view DOMAIN MODEL 43
  • 114. Real-World ArchitectureReal-World Architecture Current State RecapCurrent State Recap VIEW STORE Renders the view and calls actions on the store Holds domain state Create and modify domain state Holds and modify application state Implements business logic Fetch from API Aggregates data for the view DOMAIN MODEL 43
  • 115. Domain Model SerializationDomain Model Serialization turning JSON into observablesturning JSON into observables 44
  • 116. Domain Model SerializationDomain Model Serialization turning JSON into observablesturning JSON into observables Our state is an object tree 44
  • 117. Domain Model SerializationDomain Model Serialization turning JSON into observablesturning JSON into observables Our state is an object tree Unfortunately it's not a plain JS object 44
  • 118. Domain Model SerializationDomain Model Serialization turning JSON into observablesturning JSON into observables Our state is an object tree Unfortunately it's not a plain JS object Most APIs provide JSON as intermediate language 44
  • 119. Domain Model SerializationDomain Model Serialization turning JSON into observablesturning JSON into observables Our state is an object tree Unfortunately it's not a plain JS object Most APIs provide JSON as intermediate language We need to provide conversions to serialize or deserialize our state 44
  • 120. Domain Model SerializationDomain Model Serialization deserializationdeserialization class Todo { // ... constructor(store, data){ this.store = store if(data) Object.assign(this, data) } } 45
  • 121. Domain Model SerializationDomain Model Serialization serializationserialization class Todo { // ... get toJSON(){ return { name: this.name, done: this.done } } } // ... decorate(Todo, { toJSON: computed }) JSON is just another view of the domain model, so we can just derive itderive 46
  • 122. Domain Model SerializationDomain Model Serialization packagespackages class User { @serializable(identifier()) id = 0; @serializable name = ''; } class Todo { @serializable name = ''; @serializable done = false; @serializable(object(User)) user = null; } // You can now deserialize and serialize! const todo = deserialize(Todo, { name: 'Hello world', done: true, user: { id: 1, name: 'Mattia' } }); const todoJSON = serialize(todo) with serializr you get that with tagging your domain models 47
  • 123. Domain Model SerializationDomain Model Serialization the deserialization problemthe deserialization problem class TodoStore { todos = [] fromCache(){ const cachedData = localStorage.getItem("todos") || "[]" this.todos = JSON.parse(cachedData) .map(data => new Todo(this, data) } getById = id => this.todos .find(item => item.id === id) } decorate(TodoStore, { fromCache: action }) deserializing is memory intensive! 48
  • 124. MobX hidden featureMobX hidden feature _interceptReads_interceptReads import {_interceptReads} from "mobx" const todos = observable.map() _interceptReads(todos, value => value + "! LOL") todos.set(1, "Mattia") console.log(todos.get("1")) // => Mattia! LOL undocumented (yet there since 2017) mobx feature allows to transform mobx values while reading available for objects, arrays, maps and boxed values 49
  • 125. MobX hidden featureMobX hidden feature _interceptReads to the rescue!_interceptReads to the rescue! class TodoStore { todos = [] _cache = {} constructor(rootStore){ this.rootStore = rootStore // ... _interceptReads(this.todos, this.unboxTodo) } unboxTodo = data => { if(this._cache[data.id]){ return this._cache[data.id] } this._cache[data.id] = new Todo(this, data) return this._cache[data.id] } } 50
  • 126. MobX PerformanceMobX Performance better performant domain storesbetter performant domain stores 51
  • 127. MobX PerformanceMobX Performance better performant domain storesbetter performant domain stores Use ES6 Maps and lookup by ID when possible 51
  • 128. MobX PerformanceMobX Performance better performant domain storesbetter performant domain stores Use ES6 Maps and lookup by ID when possible Store JSON in an observable map 51
  • 129. MobX PerformanceMobX Performance better performant domain storesbetter performant domain stores Use ES6 Maps and lookup by ID when possible Store JSON in an observable map Use _interceptReads to perform lazy deserialization 51
  • 130. MobX PerformanceMobX Performance better performant domain storesbetter performant domain stores Use ES6 Maps and lookup by ID when possible Store JSON in an observable map Use _interceptReads to perform lazy deserialization Implement ID -> name without deserialization 51
  • 131. Real-World ArchitectureReal-World Architecture Current State RecapCurrent State Recap VIEW REPOSITORY Renders the view and calls actions on the store Holds domain state Create and modify domain state Fetch from API Holds and modify application state Implements business logic Aggregates data for the view STORE DOMAIN MODEL 52
  • 132. Real-World ArchitectureReal-World Architecture Current State RecapCurrent State Recap VIEW REPOSITORY Renders the view and calls actions on the store Holds domain state Create and modify domain state Fetch from API Holds and modify application state Implements business logic Aggregates data for the view STORE DOMAIN MODEL 52
  • 133. class Overview { rootStore = null currentText = "" constructor(rootStore){ this.rootStore = rootStore } get todos(){ // perform sorting and pagining here return this.rootStore.todoStore.todos } handleAddClicked(){ this.rootStore.todoStore.addTodo(this.currentText) this.currentText = "" } } decorate(Router, { todos: computed, currentText: observable, handleAddClicked: action }) 53
  • 134. Services & PresentersServices & Presenters routingrouting class Router { uri = "/" page = null // ... constructor(rootStore){ reaction(() => this.uri, uri => this.parseUri(uri)) } openOverview(){ this.page = { name: "overview", data: { project_id: 1 }} } } decorate(Router, { uri: observable, page: observable }) 54
  • 135. Services & PresentersServices & Presenters routingrouting class Router { uri = "/" page = null // ... constructor(rootStore){ reaction(() => this.uri, uri => this.parseUri(uri)) } openOverview(){ this.page = { name: "overview", data: { project_id: 1 }} } } decorate(Router, { uri: observable, page: observable }) Where load that data? Where load that data? 54
  • 136. Services & PresentersServices & Presenters view presenterview presenter class Router { uri = "/" page = null constructor(rootStore){ this.page = this.loadHome() reaction(() => this.uri, uri => this.parseUri(uri)) } openOverview(){ this.page = fromPromise( this.overviewStore.fetchData() .then(data => Promise.resolve({ name: "overview", data }) ) ) } } 55
  • 137. Services & PresentersServices & Presenters view presenterview presenter class Router { uri = "/" page = null constructor(rootStore){ this.page = this.loadHome() reaction(() => this.uri, uri => this.parseUri(uri)) } openOverview(){ this.page = fromPromise( this.overviewStore.fetchData() .then(data => Promise.resolve({ name: "overview", data }) ) ) } } 55
  • 138. Services & PresentersServices & Presenters fromPromisefromPromise class Router { uri = "/" page = null constructor(rootStore){ this.page = this.loadHome() reaction(() => this.uri, uri => this.parseUri(uri)) } openOverview(){ this.page = fromPromise( this.overviewStore.fetchData() .then(data => Promise.resolve({ name: "overview", data }) ) ) } } 56
  • 139. MobX Async TipsMobX Async Tips reactive finite state machinesreactive finite state machines <div> { this.page.case({ pending: () => "loading", rejected: (e) => "error: " + e, fulfilled: ({name, data}) => { switch(name){ case "home": return <HomePage data={data} />; case "overview": return <Overview data={data} />; } } })} </div> 57
  • 140. Real-World ArchitectureReal-World Architecture Current State RecapCurrent State Recap VIEW REPOSITORY Renders the view and calls actions on the store Holds domain state Create and modify domain state Fetch from API Holds and modify application state Implements business logic SERVICE DOMAIN MODEL PRESENTER Aggregates data for the view Call actions on the services 58
  • 142. Today's RecapToday's Recap 1. Use Redux if you're paid by the hour 59
  • 143. Today's RecapToday's Recap 1. Use Redux if you're paid by the hour 2. Find your minimal state 59
  • 144. Today's RecapToday's Recap 1. Use Redux if you're paid by the hour 2. Find your minimal state 3. Derive whatever is possible 59
  • 145. Today's RecapToday's Recap 1. Use Redux if you're paid by the hour 2. Find your minimal state 3. Derive whatever is possible 4. Think as you're building for any UI environment 59
  • 146. Today's RecapToday's Recap 1. Use Redux if you're paid by the hour 2. Find your minimal state 3. Derive whatever is possible 4. Think as you're building for any UI environment 5. Always experiment and have fun! 59
  • 147. Want something opinionated?Want something opinionated? 1. Define store shape 2. Automatic serialization/deserialization 3. Implements both a immutable & mutable store 4. Defines a stricter architecture 5. Time travelling discover mobx-state-treediscover mobx-state-tree 60
  • 148. Not into mutable things?Not into mutable things? stay here for the next talk!stay here for the next talk! 61
  • 149. Thanks for your time!Thanks for your time! Questions?Questions?   @MattiaManzati - slides.com/mattiamanzati 62