react state management with redux and mobx
TRANSCRIPT
React State Management with Redux and MobX
DARKO KUKOVEC @DarkoKukovec ANDREI ZVONIMIR CRNKOVIĆ @andreicek
INFINUM @infinumco
DARKO KUKOVEC JS Team Lead
@DarkoKukovec
ANDREI ZVONIMIR CRNKOVIĆ JS Emojineer @andreicek
We're an independent design & development agency.
Topic
Time
SCHEDULE
13:45-14:30 14:30-15:15 15:15-16:00
React setState React + Redux React + MobX
The Joy of Optimizing
Una Kravets (DigitalOcean)
Coffee.js? How I hacked my coffee machine using
JavaScript
Dominik Kundel (Twilio)
GraphQL: Data in modern times
Dotan Simha (The Guild)
01APP STATE
APP STATE
• Data • Which user is logged in? • Which todos did the user create?
• UI • Which todos is the user looking at (filter - all, complete, incomplete)
02STATE MANAGEMENT
STATE MANAGEMENT
• What should happen when some data changes?
• Does the UI need to be updated
• Does some other data depend on it?
• Does the app need to make an action (e.g. API call)?
03WHY DO WE NEED IT?
IMAGE GOES INSIDE THIS BOX DELETE BOX AFTER PLACING IMAGE
• Todos
• Title
• Status
• Active filter
• Number of todos
Handing an action in the app
Handing an action in the app
• The user checks a todo as complete
Handing an action in the app
• The user checks a todo as complete
• Mark the todo as complete
Handing an action in the app
• The user checks a todo as complete
• Mark the todo as complete
• Update the incomplete items count
Handing an action in the app
• The user checks a todo as complete
• Mark the todo as complete
• Update the incomplete items count
• What filter is active again?
Handing an action in the app
• The user checks a todo as complete
• Mark the todo as complete
• Update the incomplete items count
• What filter is active again?
• Do I need to update the list of todos?
• Add/remove item?
• Sort items?
• Should I show an empty state?
• Should I make an API call?
• Should I save the change into
localStorage?
Data Flow in JavaScript Applications - Ryan Christiani
04EXAMPLE PROJECT
EXAMPLE PROJECT
• Tech conference app
• Fetch talk list (async action)
• Favourite talks (simple sync action)
• Filter by location (filtering data from the state)
• To simplify • No routing • No forms • Not responsive • Only basic styling • Assumption: Browsers support Fetch API
• 70% of browsers do, polyfill available for the rest
EXAMPLE PROJECT - TECH STUFF
• create-react-app as the initial setup
• https://github.com/infinum/shift-2017
05BEFORE WE START...
COMPONENT TYPES
Container Presenter
Usually root components Inside of container components
No UI Only UI
State of children components Dumb component
Handle state changes "Data down, actions up"
06PREREQUISITES
PREREQUISITES
• A modern IDE (VS Code, Sublime Text, Atom, Webstorm or similar)
• Latest version of Chrome/Chromium for debugging
• Node.js 6 or 7
• npm 4 or yarn
07REACT + SETSTATE
REACT + SETSTATE
• No central state
• Every component contains its (and children?) state
• State changes are async! - 2nd argument is a callback
• Component is re-rendered unless shouldComponentUpdate() returns false
• Additional libs • react-addons-update
// Code time! // setState // Code used for setup: create-react-app app-setstate npm install --save react-addons-update
v1.0
FUNCTIONAL SETSTATE
• Since React 15?
• Will eventually replace the usage with objects as the first argument
class App extends Component { constructor(props) { super(props); this.state = {counter: 0}; }
increaseCounter() { this.setState({ counter: this.state.counter + 1 }); }
decreaseCounter() { this.setState((prevState, props) => { return {counter: prevState.counter + 1}; }); }
render() { // ... } }
SCALING SETSTATE
• A bad idea • One master component with app state • Some smaller independent containers
08MOBX VS REDUX
http:!//!!www.timqian.com/star-history/#mobxjs/mobx&reactjs/redux&facebook/flux&facebook/relay
ADVANTAGES
Easier to (unit) test
MobX Redux
Faster
Less boilerplate Smaller
Time travelMore flexible
Easier to debug?Simpler async actions
IN THE NUTSHELL...
https:!//twitter.com/phillip_webb/status/705909774001377280
USAGE
MobX Redux
mobx-react react-redux
Not React dependent Not React dependent
mobx-angular
Python
GWT
09REACT + REDUX
REACT + REDUX
• One central state
• Additional libs • redux • react-redux • redux-thunk
STORE Holds application state
ACTIONS Payloads of information that send data from your
application to your store
REDUCERS Specify how the application's state changes in
response to actions
import { createStore } from 'redux'
function counter(state = 0, action) { switch (action.type) { case 'INCREMENT': return state + 1 case 'DECREMENT': return state - 1 default: return state } }
let store = createStore(counter)
store.subscribe(() => console.log(store.getState()) )
store.dispatch({ type: 'INCREMENT' }) // 1 store.dispatch({ type: 'INCREMENT' }) // 2 store.dispatch({ type: 'DECREMENT' }) // 1
THE GIST
// Code time! // Redux // Code used for setup: create-react-app app-redux npm install --save redux react-redux redux-thunk
v2.0
* Mock data * Styles * Presenter components * Utils
v2.0
// Redux DevTools // https://chrome.google.com/webstore
https:!//chrome.google.com/webstore/detail/redux-devtools/lmhkpmbekcpmknklioeibfkpmmfibljd?hl=env2.6
SCALING REDUX
• Only one store!
• Multiple (nested) reducers, actions • Store data nesting === reducer nesting
• "reducers" folder, "actions" folder
• Folders based on functionality • e.g. "orders" contains a orders reducer, orders actions and all other related stuff
THINGS TO KEEP IN MIND
• NEVER mutate the state!
• Immutable.js
09REDUX HELPER LIBS
IMMUTABLE.JS
• https://github.com/facebook/immutable-js/
• List, Stack, Map, OrderedMap, Set, OrderedSet, Record
• Nested structures
REDUX FORM
• https://github.com/erikras/redux-form
• Uses Immutable.js
• Sync/async validations
• Wizard forms
REDUX - THE SOURCE CODE
https:!//twitter.com/bloodyowl/status/740228059538857984
10REACT + MOBX
REACT + MOBX
• One/multiple states
• Additional libs • mobx • mobx-react
Anything that can be derived from the application state, should be derived. Automatically.
- The philosophy behind MobX
OBSERVABLE Your state
e.g., list of TODOs
ACTION A function that changes the state
e.g., function called after the user clicks on the completed checkbox
COMPUTED PROPERTY A value derived from your state e.g., list of completed TODOs
OBSERVER Function (component?) that needs to be updated
on state change e.g., component showing a list of TODOs
STATE (OBSERVABLES)
COMPUTED PROPS
OBSERVERSACTION
UPDATES
UPDATESUPDATESCHANGES
THE GIST
import { observable, autorun } from 'mobx';
const store = observable({counter: 0});
autorun(() => console.log(store.counter); );
store.counter++; // 1 store.counter++; // 2 store.counter--; // 1
import { createStore } from 'redux'
function counter(state = 0, action) { switch (action.type) { case 'INCREMENT': return state + 1 case 'DECREMENT': return state - 1 default: return state } }
let store = createStore(counter)
store.subscribe(() => console.log(store.getState()) )
store.dispatch({ type: 'INCREMENT' }) // 1 store.dispatch({ type: 'INCREMENT' }) // 2 store.dispatch({ type: 'DECREMENT' }) // 1
THE GIST
import { observable, autorun } from 'mobx';
const store = observable({counter: 0});
autorun(() => console.log(store.counter); );
store.counter++; // 1 store.counter++; // 2 store.counter--; // 1
MobX Redux
DECORATORS JS proposed feature (Babel plugin)
TypeScript feature
import {observable, computed} from 'mobx';
class Todos { @observable list = []; users = observable([]);
@computed get complete() { return this.list.filter((todo) => todo.complete); }
incomplete: computed(() => { return this.list.filter((todo) => !todo.complete); }) }
// Code time! // MobX // Code used for setup: create-react-app app-mobx --scripts-version custom-react-scripts
npm install --save mobx mobx-react
v3.0
* Mock data * Styles * Presenter components * Utils
v3.0
// Actions
v3.2
THINGS TO KEEP IN MIND
• use extendObservable to add properties to an object
• Wrapped objects - isArray(arr) or array.is(toJS(arr)) • Don't be afraid to use observers
• If done right: More observers → better performance
SCALING MOBX
• Your code can be object oriented!
• A list of best practices
• Strict mode, actions (transactions)
• Use helper libs
10MOBX HELPER LIBS
MOBX-REACT-DEVTOOLS
• Log actions & reactions
• Check dependencies for a react component
• Show update times
"STRUCTURE" LIBS Opinionated
MOBX-COLLECTION-STORE
• https://github.com/infinum/mobx-collection-store
• One collection, multiple model types
• Relationships between models
• mobx-jsonapi-store
• https://github.com/infinum/mobx-jsonapi-store
MOBX-STATE-TREE
• https://github.com/mobxjs/mobx-state-tree
• Made by MobX authors
• v1.0 release soon
• Supports snapshots, replay, JSON patches, etc.
• Supports time travel! • Compatible with Redux DevTools!
MOBX-REACT-FORM
• https://github.com/foxhound87/mobx-react-form
• Dedicated DevTools: https://github.com/foxhound87/mobx-react-form-devtools
REACT SETSTATE + MOBX
• You can still use it
• But, you can also do this…
import {observable} from 'mobx'; import {observer} from 'mobx-react'; import React, {Component} from 'react';
@observer export default class Foo extends Component { @observable state = { clickCount = 0; };
onClick: () => { this.state.clickCount++; // if you use setState it will stop being an observable! }
render() { return ( <button onClick={this.onClick}> { this.state.clickCount ? `Clicked ${this.state.clickCount} times!` : 'Click me!' } </button> ); } }
RESOURCES
• Redux • http://redux.js.org/ • https://egghead.io/courses/getting-started-with-redux • https://egghead.io/courses/building-react-applications-with-idiomatic-redux
• MobX • https://mobx.js.org/ • https://mobxjs.github.io/mobx/getting-started.html • https://egghead.io/courses/manage-complex-state-in-react-apps-with-mobx
Visit infinum.co or find us on social networks:
infinum.co infinumco infinumco infinum
Thank you! [email protected] @DARKOKUKOVEC
[email protected] @ANDREICEK