SlideShare a Scribd company logo
6/29/2017 DevCamp 2017
https://ohager.github.io/devcamp2017_slides/#/?export& 1/55
EFFECTIVE
APPLICATION STATE
MANAGEMENT
Oliver Hager - Dextra 🤘
6/29/2017 DevCamp 2017
https://ohager.github.io/devcamp2017_slides/#/?export& 2/55
O que é o estado de aplicação?
6/29/2017 DevCamp 2017
https://ohager.github.io/devcamp2017_slides/#/?export& 3/55
6/29/2017 DevCamp 2017
https://ohager.github.io/devcamp2017_slides/#/?export& 4/55
6/29/2017 DevCamp 2017
https://ohager.github.io/devcamp2017_slides/#/?export& 5/55
LIÇÃO 1
6/29/2017 DevCamp 2017
https://ohager.github.io/devcamp2017_slides/#/?export& 6/55
BAD 💩
render(){
return (
this.state.userRole === 'admin' ?
<AdminComponent/> :
<UserComponent/>
)
}
6/29/2017 DevCamp 2017
https://ohager.github.io/devcamp2017_slides/#/?export& 7/55
GOOD 👍
render(){
return (
this.state.userIsAdmin ?
<AdminComponent/> :
<UserComponent/>
)
}
6/29/2017 DevCamp 2017
https://ohager.github.io/devcamp2017_slides/#/?export& 8/55
render(){
function calculateTotalCost(items){
return items.reduce(
(p,item) => p+(item.price*item.quantity), 0.0
).toFixed(2);
}
return (
<div>
<CartItemList items={this.state.cartItems}/>
<div className='cart-total'>{
calculateTotalCost(this.state.cartItems) + '$'
}<div/>
<div/>
)
}
6/29/2017 DevCamp 2017
https://ohager.github.io/devcamp2017_slides/#/?export& 9/55
⭐ Mantenha mais simples possível, p.e. booleano
6/29/2017 DevCamp 2017
https://ohager.github.io/devcamp2017_slides/#/?export& 10/55
LIÇÃO 2
Compose your state
6/29/2017 DevCamp 2017
https://ohager.github.io/devcamp2017_slides/#/?export& 11/55
TRIVIAL 👍
const update = (state, newState) => Object.assign({}, state, newState);
const updateState = (state, newState) => ({ ...state, ...newState }); //
6/29/2017 DevCamp 2017
https://ohager.github.io/devcamp2017_slides/#/?export& 12/55
ASSIGN/... VS MERGE
New Repl ohagermy repls community
BETA
DevCamp2017: Assign vs Merge
share save run
Babel Compiler v6.4.4
Copyright (c) 2014-2015 Sebastian McKenzie
6/29/2017 DevCamp 2017
https://ohager.github.io/devcamp2017_slides/#/?export& 13/55
⭐ Use composição para montar estado
6/29/2017 DevCamp 2017
https://ohager.github.io/devcamp2017_slides/#/?export& 14/55
LIÇÃO 3
Make your state immutable
6/29/2017 DevCamp 2017
https://ohager.github.io/devcamp2017_slides/#/?export& 15/55
6/29/2017 DevCamp 2017
https://ohager.github.io/devcamp2017_slides/#/?export& 16/55
class ItemList extends Component {
constructor() {
this.state.items = Store.getState().items
}
// assume this method is called when our store was updated
onStoreUpdate() {
this.setState({items: Store.getState().items })
}
addItem(item) {
let items = this.state.items
items.push(item) // Bad 💩
}
render() {
return (
<ItemEditor onAdd = {this.addItem} />
/*...*/
)
}
}
6/29/2017 DevCamp 2017
https://ohager.github.io/devcamp2017_slides/#/?export& 17/55
class ItemList extends Component {
constructor() {
this.state.items = Store.getState().items
}
// assume this method is called when our store was updated
onStoreUpdate() {
this.setState({items: Store.getState().items })
}
addItem(item) {
let items = this.state.items
items.push(item) // State mutated in STORE!
}
render() {
return (
<ItemEditor onAdd = {this.addItem} />
/*...*/
)
}
}
6/29/2017 DevCamp 2017
https://ohager.github.io/devcamp2017_slides/#/?export& 18/55
SOLUÇÃO SUBÓTIMA 😕
addItem(item) {
let items = _.cloneDeep(this.state.items)
items.push(item) // store won't be mutated
Store.update({items: items})
}
6/29/2017 DevCamp 2017
https://ohager.github.io/devcamp2017_slides/#/?export& 19/55
SOLUÇÃO MELHOR 😊
constructor() {
// Copy by reference for immutable data is ok!
this.state.items = Store.getImmutableState().get('items')
}
// ...
addItem(item) {
// you cannot simply add an item to the list, you need to use the API
let copiedItems = Immutable(this.state.items).asMutable()
copiedItems.push(item)
// assumes that update() makes the state somehow immutable
Store.update({items: copiedItems})
}
6/29/2017 DevCamp 2017
https://ohager.github.io/devcamp2017_slides/#/?export& 20/55
COMO FAZER IMUTÁVEL? 🤔
New Repl ohagermy repls community
BETA
DevCamp2017: Immutable
share save run
Babel Compiler v6.4.4
Copyright (c) 2014-2015 Sebastian McKenzie
6/29/2017 DevCamp 2017
https://ohager.github.io/devcamp2017_slides/#/?export& 21/55
COMO FAZER IMUTÁVEL? 🤔
https://github.com/facebook/immutable-js [60KiB]
https://github.com/rtfeldman/seamless-immutable [8KiB]
https://github.com/Yomguithereal/baobab [33KiB]
https://github.com/planttheidea/crio [51KiB]
... etc etc pp
6/29/2017 DevCamp 2017
https://ohager.github.io/devcamp2017_slides/#/?export& 22/55
THE GOOD THINGS 🍺
// optimize our rendering process (React-wise)
shouldComponentUpdate(nextProps, nextState){
// comparing by reference
return this.state.items !== nextState.items;
}
⭐ Evite alterações acidentais (bom para times)
6/29/2017 DevCamp 2017
https://ohager.github.io/devcamp2017_slides/#/?export& 23/55
THE BAD THING(S) 🍋
⭐ Mais verboso/trabalhoso para alterar
6/29/2017 DevCamp 2017
https://ohager.github.io/devcamp2017_slides/#/?export& 24/55
LIÇÃO 4
Flatten the state
Keep it simple 2
6/29/2017 DevCamp 2017
https://ohager.github.io/devcamp2017_slides/#/?export& 25/55
const posts = [
{
"id": 123,
"author": {
"id": 1,
"name": "Paul"
},
"title": "My awesome blog post",
"comments": [
{
"id": 324,
"commenter": {
"id": 2,
"name": "Nicole",
"text" : "Nicole + Paul = Love 💕"
}
},
{
"id": 325,
"commenter": {
"id": 5,
"name": "Klaus",
"text" : "Yo, Mann. Geiler Scheiss!"
}
}
]
}
];
PROBLEM👾
6/29/2017 DevCamp 2017
https://ohager.github.io/devcamp2017_slides/#/?export& 26/55
UPDATE
posts.find( p => p.id === 123).author.name = 'Dude';
UPDATE IMMUTABLE
// update
const post = posts.find( p => p.id === 123);
// seamless-immutable API
const updatedPost = Immutable.setIn(post, ["author", "name"], "Elmar");
// insert
const atIndex = posts.findIndex( p => p.id === 123);
const newPost = { /* ... */ };
posts.slice(0, atIndex).concat(newPost).concat(posts.slice(atIndex+1)); // 😲 WTF?
6/29/2017 DevCamp 2017
https://ohager.github.io/devcamp2017_slides/#/?export& 27/55
const normalizedPosts = {
"posts": [
{
"id": 123,
"authorId": 1,
"title": "My awesome blog post",
"comments": [324, 325]
}
// ... more posts
],
"comments": [
{
"id": 324,
"authorId": 2
}, {
"id": 325,
"authorId": 5
}
],
"authors": [
{
"id": 1,
"name": "Paul"
},
{
"id": 2,
"name": "Nicole"
}, {
FLATTENED/NORMALIZED
6/29/2017 DevCamp 2017
https://ohager.github.io/devcamp2017_slides/#/?export& 28/55
⭐ "Estados rasos" simpli cam acesso/alterações
6/29/2017 DevCamp 2017
https://ohager.github.io/devcamp2017_slides/#/?export& 29/55
LIÇÃO 5
Determine your state's scope
6/29/2017 DevCamp 2017
https://ohager.github.io/devcamp2017_slides/#/?export& 30/55
STORY TIME
6/29/2017 DevCamp 2017
https://ohager.github.io/devcamp2017_slides/#/?export& 31/55
ONCE UPON A TIME
⚫ Quis criar um
suggestion/autocomplete
6/29/2017 DevCamp 2017
https://ohager.github.io/devcamp2017_slides/#/?export& 32/55
SO SAD
6/29/2017 DevCamp 2017
https://ohager.github.io/devcamp2017_slides/#/?export& 33/55
ACTION CREATOR
// Action Creator - ES5 old school style, babe
function fetchSuggestions(componentId, url, searchTerm){
var suggestionService = new GenericService(url);
suggestionService.getAll({
textFilter: searchTerm,
//...
})
.then(function (suggestions) {
this.dispatch('fetchSuggestions', {
componentId : componentId, // Hu, I need to manage the component instance!
suggestions : suggestions
});
}.bind(this));
}
6/29/2017 DevCamp 2017
https://ohager.github.io/devcamp2017_slides/#/?export& 34/55
SUGGESTION STORE
var _suggestions = {};
var suggestionStore = {
// get the suggestions for specific component
getSuggestions : function(componentId){ // WTF?
return _suggestions[componentId]; // WTF?
},
// called on dispatch('fetchSuggestions',...)
// -- nanoflux uses a convention based auto-mapping
onFetchSuggestions : function(data){
_suggestions[data.componentId] = Immutable(data.suggestions); // WTF?
this.notifyListeners();
}
};
6/29/2017 DevCamp 2017
https://ohager.github.io/devcamp2017_slides/#/?export& 35/55
⭐ A LISTA DE SUGESTÕES NÃO É
ESTADO DA APLICAÇÃO
6/29/2017 DevCamp 2017
https://ohager.github.io/devcamp2017_slides/#/?export& 36/55
⭐ A LISTA DE SUGESTÕES É ESTADO
DO COMPONENTE!
6/29/2017 DevCamp 2017
https://ohager.github.io/devcamp2017_slides/#/?export& 37/55
COMO DETERMINAR O ESCOPO DO
ESTADO?
Componente Aplicação
6/29/2017 DevCamp 2017
https://ohager.github.io/devcamp2017_slides/#/?export& 38/55
LIÇÃO 6
Manage your state globally
6/29/2017 DevCamp 2017
https://ohager.github.io/devcamp2017_slides/#/?export& 39/55
6/29/2017 DevCamp 2017
https://ohager.github.io/devcamp2017_slides/#/?export& 40/55
BASIC PRINCIPLES
⭐ Deixar acessível por todos os componentes
⭐ Alterar o estado apenas em um único ponto bem de nido
6/29/2017 DevCamp 2017
https://ohager.github.io/devcamp2017_slides/#/?export& 41/55
ACESSO AO ESTADO
Reactive - Hollywood principle: Don't call us, we call you! 📞
6/29/2017 DevCamp 2017
https://ohager.github.io/devcamp2017_slides/#/?export& 42/55
OBSERVER/PUBSUB
function notify(listeners, state) {
for(const pn in listeners){
const l = listeners[pn]
l.f.call(l.c, state)
}
}
class ApplicationStateManager {
constructor(){
this._listenerCount = 0;
this._listeners = {};
this._state = {};
}
listen(fn, ctx) {
// use object map instead of array (is smaller and faster)
this._listeners[++this._listenerCount] = { f: fn, c: ctx }
return this._listenerCount
}
unlisten(listenerId) {
delete this._listeners[listenerId];
}
update(args){
//... update stuff
notify(this._listeners, this._state)
}
getState() { return this._state }
}
6/29/2017 DevCamp 2017
https://ohager.github.io/devcamp2017_slides/#/?export& 43/55
USING BROWSER EVENTS
class ApplicationStateManager {
constructor(){
this._state = {};
}
update(args){
//... update stuff
window.dispatchEvent(new CustomEvent('stateUpdate', { 'detail': this._state }) )
}
getState() { return this._state }
}
// Usage
const mgr = new ApplicationStateManager();
window.addEventListener('stateUpdate', e => { console.log('state updated', e.detail)} )
mgr.update( {} ) // fake update here!
window.removeEventListener('stateUpdate');
6/29/2017 DevCamp 2017
https://ohager.github.io/devcamp2017_slides/#/?export& 44/55
ALTERAR O ESTADO
"Single Source of Truth"🍺
6/29/2017 DevCamp 2017
https://ohager.github.io/devcamp2017_slides/#/?export& 45/55
function deepFreeze(obj) {
for(let pn in obj) {
if (typeof obj[pn] === 'object')
deepFreeze(obj[pn])
}
return Object.freeze(obj)
}
class ApplicationStateManager {
constructor(){
// ...
this._state = {};
}
/* ... */
update(fn){
let s = this._state;
s = deepFreeze(Object.assign({}, s, fn(s)))
notify(this._listeners, s)
}
getState() { return this._state } // immutable, YAY!
}
UPDATE
6/29/2017 DevCamp 2017
https://ohager.github.io/devcamp2017_slides/#/?export& 46/55
UPDATE USAGE
function addItem(newItem){
appStateManager.update( state => {
// need to copy the state, as it is immutable
let items = _.cloneDeep(state.items || []);
items.push(newItem);
return {items: items}; // return new state slice
})
}
6/29/2017 DevCamp 2017
https://ohager.github.io/devcamp2017_slides/#/?export& 47/55
BASIC PRINCIPLES RECAP
⭐ Acesso ao estado de forma reativa com Observer/PubSub ou similar
⭐ Estado alterável apenas pelo próprio State Manager (imutabilidade força)
6/29/2017 DevCamp 2017
https://ohager.github.io/devcamp2017_slides/#/?export& 48/55
LIÇÃO 7
Break huge states in parts
6/29/2017 DevCamp 2017
https://ohager.github.io/devcamp2017_slides/#/?export& 49/55
BEST PRACTICES OF THE TOP DOGS
https://facebook.github.io/ ux/docs/ ux-utils.html#store
http://redux.js.org/docs/recipes/reducers/UsingCombineReducers.htm
https://mobx.js.org/best/store.html
⭐ Flux suporta múltiplos "Stores"
⭐ Redux promove vários "Reducers"
⭐ MobX recomenda "Domain Stores"
6/29/2017 DevCamp 2017
https://ohager.github.io/devcamp2017_slides/#/?export& 50/55
SEPARAÇÃO POR CONTEXTO -
APLICAÇÃO
Noti cações globais, Modais, Indicadores de Carregamento, etc.
6/29/2017 DevCamp 2017
https://ohager.github.io/devcamp2017_slides/#/?export& 51/55
SEPARAÇÃO POR CONTEXTO -
DOMÍNIO
Pedidos, Serviços, Produtos, Compras, Vendas etc.
6/29/2017 DevCamp 2017
https://ohager.github.io/devcamp2017_slides/#/?export& 52/55
RESUMO
6/29/2017 DevCamp 2017
https://ohager.github.io/devcamp2017_slides/#/?export& 53/55
JUST FOR FUN!
Stappo Demo
This small demo is built with RiotJS, BlazeCSS and Webpack
Clear All Add
Your items Stappo Stats
npmnpm v0.0.7v0.0.7
Build Type Size [bytes]
Generic Bundle 1271
Web Bundle 1243
Generic 337
Web 274
Enter name of item to add...
No Items Here
What are you searching for?
6/29/2017 DevCamp 2017
https://ohager.github.io/devcamp2017_slides/#/?export& 54/55
274 BYTES!
function Stappo(a){function d(b){for(var a in
b)"object"==typeof b[a]&&d(b[a]);return
Object.freeze(b)}a=void 0===a?"stappo":a;var c=
{};this.update=function(b)
{c=d(Object.assign({},c,b()));window.dispatchEvent(new
CustomEvent(a,{detail:c}))};this.get=function(){return c}};
Fully reactive App State Manager with immutable states
https://github.com/ohager/stappo
6/29/2017 DevCamp 2017
https://ohager.github.io/devcamp2017_slides/#/?export& 55/55
DANKE!🤘
github.com/ohager
linkedin.com/in/oliverhager
devbutze.com

More Related Content

PPTX
Using github development process in your company
PDF
Collaborative Development: The Only CD That Matters - Brent Beer - Codemotion...
PDF
Front Ends for Back End Developers - Spring I/O 2017
PPTX
Container based CI/CD on GitHub Actions
PDF
OSCONF - April 2021 - Run GitHub Actions Locally with nektos/act and Docker
PDF
少し幸せになる技術
PDF
Introduction to Continuous Integration with Jenkins
PDF
Bootiful Development with Spring Boot and React - RWX 2017
Using github development process in your company
Collaborative Development: The Only CD That Matters - Brent Beer - Codemotion...
Front Ends for Back End Developers - Spring I/O 2017
Container based CI/CD on GitHub Actions
OSCONF - April 2021 - Run GitHub Actions Locally with nektos/act and Docker
少し幸せになる技術
Introduction to Continuous Integration with Jenkins
Bootiful Development with Spring Boot and React - RWX 2017

What's hot (20)

PDF
Introduction à l’intégration continue avec Jenkins
PDF
Front End Development for Back End Developers - UberConf 2017
PDF
Introduction à l'intégration continue en PHP
PPTX
Git Merge, Resets and Branches
PDF
GitHub Pull Request Builder for Drupal
PDF
Workshop presentation
PDF
Bootiful Development with Spring Boot and Angular - Spring I/O 2017
PDF
Bootiful Development with Spring Boot and Vue - RWX 2018
ODP
Hot Reloading with React - Experiences
PDF
Development with Git and Gerrit - Eclipse DemoCamp Stuttgart - 2010-11-23
PDF
WSO2Con ASIA 2016: Automate and Orchestrate DevOps
PDF
Microservices for the Masses with Spring Boot and JHipster - RWX 2018
PPTX
Intro. to Git and Github
PDF
What's New in JHipsterLand - Devoxx Poland 2017
PDF
DevOps 及 TDD 開發流程哲學
PDF
Front End Development for Back End Developers - Devoxx UK 2017
PDF
Instances Behind the Scene: What happen when you click on «create a new insta...
PDF
It's Not Continuous Delivery If You Can't Deploy Right Now
PDF
[Ultracode Munich #4] Short introduction to the new Android build system incl...
PDF
Entwicklung mit Android Studio und Gradle
Introduction à l’intégration continue avec Jenkins
Front End Development for Back End Developers - UberConf 2017
Introduction à l'intégration continue en PHP
Git Merge, Resets and Branches
GitHub Pull Request Builder for Drupal
Workshop presentation
Bootiful Development with Spring Boot and Angular - Spring I/O 2017
Bootiful Development with Spring Boot and Vue - RWX 2018
Hot Reloading with React - Experiences
Development with Git and Gerrit - Eclipse DemoCamp Stuttgart - 2010-11-23
WSO2Con ASIA 2016: Automate and Orchestrate DevOps
Microservices for the Masses with Spring Boot and JHipster - RWX 2018
Intro. to Git and Github
What's New in JHipsterLand - Devoxx Poland 2017
DevOps 及 TDD 開發流程哲學
Front End Development for Back End Developers - Devoxx UK 2017
Instances Behind the Scene: What happen when you click on «create a new insta...
It's Not Continuous Delivery If You Can't Deploy Right Now
[Ultracode Munich #4] Short introduction to the new Android build system incl...
Entwicklung mit Android Studio und Gradle
Ad

Similar to Effective Application State Management (@DevCamp2017) (20)

PPTX
Battle of React State Managers in frontend applications
PPTX
Adding a modern twist to legacy web applications
PPTX
React & redux
PDF
Strategies for Mitigating Complexity in React Based Redux Applicaitons
PPTX
One code Web, iOS, Android
PDF
Higher-Order Components — Ilya Gelman
PPTX
Build web apps with react js
PPTX
Thinking in react
PDF
Enemy of the state
PDF
Recompacting your react application
PPTX
React.js at Cortex
PPTX
Basics Of Introduction to ASP.NET Core.pptx
PDF
Sexy React Stack
PPTX
GraphQL - Where are you from? Where are you going?
PDF
An Intense Overview of the React Ecosystem
PDF
Reactive Programming - ReactFoo 2020 - Aziz Khambati
PPTX
The productive developer guide to React
PDF
Making react part of something greater
PDF
Workshop 19: ReactJS Introduction
PDF
Introduction to Redux (for Angular and React devs)
Battle of React State Managers in frontend applications
Adding a modern twist to legacy web applications
React & redux
Strategies for Mitigating Complexity in React Based Redux Applicaitons
One code Web, iOS, Android
Higher-Order Components — Ilya Gelman
Build web apps with react js
Thinking in react
Enemy of the state
Recompacting your react application
React.js at Cortex
Basics Of Introduction to ASP.NET Core.pptx
Sexy React Stack
GraphQL - Where are you from? Where are you going?
An Intense Overview of the React Ecosystem
Reactive Programming - ReactFoo 2020 - Aziz Khambati
The productive developer guide to React
Making react part of something greater
Workshop 19: ReactJS Introduction
Introduction to Redux (for Angular and React devs)
Ad

Recently uploaded (20)

PDF
Understanding Forklifts - TECH EHS Solution
PDF
Digital Strategies for Manufacturing Companies
PDF
T3DD25 TYPO3 Content Blocks - Deep Dive by André Kraus
PDF
How to Migrate SBCGlobal Email to Yahoo Easily
PDF
Adobe Illustrator 28.6 Crack My Vision of Vector Design
PPTX
assetexplorer- product-overview - presentation
PDF
Raksha Bandhan Grocery Pricing Trends in India 2025.pdf
PDF
Design an Analysis of Algorithms II-SECS-1021-03
PDF
Claude Code: Everyone is a 10x Developer - A Comprehensive AI-Powered CLI Tool
PDF
Internet Downloader Manager (IDM) Crack 6.42 Build 42 Updates Latest 2025
PDF
top salesforce developer skills in 2025.pdf
PDF
System and Network Administration Chapter 2
PDF
Softaken Excel to vCard Converter Software.pdf
PPTX
Operating system designcfffgfgggggggvggggggggg
PPTX
Computer Software and OS of computer science of grade 11.pptx
PDF
Which alternative to Crystal Reports is best for small or large businesses.pdf
PDF
PTS Company Brochure 2025 (1).pdf.......
PPTX
CHAPTER 2 - PM Management and IT Context
PDF
Designing Intelligence for the Shop Floor.pdf
PDF
Upgrade and Innovation Strategies for SAP ERP Customers
Understanding Forklifts - TECH EHS Solution
Digital Strategies for Manufacturing Companies
T3DD25 TYPO3 Content Blocks - Deep Dive by André Kraus
How to Migrate SBCGlobal Email to Yahoo Easily
Adobe Illustrator 28.6 Crack My Vision of Vector Design
assetexplorer- product-overview - presentation
Raksha Bandhan Grocery Pricing Trends in India 2025.pdf
Design an Analysis of Algorithms II-SECS-1021-03
Claude Code: Everyone is a 10x Developer - A Comprehensive AI-Powered CLI Tool
Internet Downloader Manager (IDM) Crack 6.42 Build 42 Updates Latest 2025
top salesforce developer skills in 2025.pdf
System and Network Administration Chapter 2
Softaken Excel to vCard Converter Software.pdf
Operating system designcfffgfgggggggvggggggggg
Computer Software and OS of computer science of grade 11.pptx
Which alternative to Crystal Reports is best for small or large businesses.pdf
PTS Company Brochure 2025 (1).pdf.......
CHAPTER 2 - PM Management and IT Context
Designing Intelligence for the Shop Floor.pdf
Upgrade and Innovation Strategies for SAP ERP Customers

Effective Application State Management (@DevCamp2017)