Upgrade to Pro — share decks privately, control downloads, hide ads and more …

4 Faces of the State Management in React

4 Faces of the State Management in React

In React you can manage application's state in many ways. This talk will present you the 4 most popular solutions, with their pros and cons. All this upheld by real case studies.

mciastek

July 25, 2018
Tweet

More Decks by mciastek

Other Decks in Technology

Transcript

  1. Who am I • 4+ years of experience • projects

    in React, Angular (1&2+), Ember and Vue.js • 7+ projects in React • speaker on Front-Trends, meet.js and others
  2. The State In information technology and computer science, a program

    is described as stateful if it is designed to remember preceding events or user interactions the remembered information is called the state of the system. • Digital logic circuit state • Program state • Finite state machines https://en.wikipedia.org/wiki/State_(computer_science)
  3. What is State Management • refers to management of the

    application’s state • defines rules how to manage the state • applies not only to UI state, but also to business logic in front-end app • describes how application behaves
  4. • Session • Application Object • Caching Back-end Types of

    State • View state • Cookies • Local / Session Storage Front-end https://www.c-sharpcorner.com/UploadFile/de41d6/view-state-vs-session-state-vs-application-state/
  5. • unidirectional data flow • events or actions are the

    main players • store does all calculations • can be implemented as completely asynchronous Flux MVC vs Flux • bidirectional data flow (two- way binding) • data binding is the key • somewhat synchronous MVC https://syndicode.com/2017/09/19/flux-vs-mvc/
  6. www.swingdev.io import React, { Fragment } from 'react' class Counter

    extends React.Component { constructor(props) { super(props) this.state = { count: 0 } } increment = () => { this.setState(prevState => ({ count: prevState.count + 1 })) } decrement = () => { this.setState(prevState => ({ count: prevState.count - 1 })) } render() { return ( <Fragment> <button onClick={() => this.increment()}>+1</button> <span>{this.state.count}</span> <button onClick={() => this.decrement()}>-1</button> </Fragment> ) } } export default Counter
  7. • tricky to pass state from one component to others

    • leads to multiple sources of truth • causes unnecessary renders Cons Pros • easy to learn and use - you probably know it • accessible in all React components • it’s great for maintain state of UI components
  8. www.swingdev.io export default (state = 0, action) => { switch

    (action.type) { case 'INCREMENT': return state + 1 case 'DECREMENT': return state - 1 default: return state } }
  9. www.swingdev.io import React from 'react' import { connect } from

    'react-redux' const Counter = (props) => ( <div> <button onClick={() => props.increment()}>+1</button> <span>{props.counter}</span> <button onClick={() => props.decrement()}>-1</button> </div> ) const mapStateToProps = (state) => ({ counter: state.counter }) const mapDispatchToProps = (dispatch) => ({ increment: () => dispatch({ type: 'INCREMENT' }), decrement: () => dispatch({ type: 'DECREMENT' }), }) export default connect(mapStateToProps, mapDispatchToProps)(Counter)
  10. • can end with a lot of boilerplate • doesn’t

    handle side-effect by default • immutable Cons Pros • enforces single source of truth • functional programming practices • immutable
  11. www.swingdev.io import { observable } from 'mobx' class CounterStore {

    @observable counter = 0; increment() { this.counter += 1 } decrement() { this.counter -= 1 } } export default new CounterStore()
  12. www.swingdev.io import React from 'react' import { observer } from

    'mobx-react' import store from './store.js' const Counter = () => ( <div> <button onClick={() => store.increment()}>+1</button> <span>{store.counter}</span> <button onClick={() => store.decrement()}>-1</button> </div> ) export default observer(Counter)
  13. • tricky middlewares • Observable object parsing • mutable Cons

    Pros • less boilerplate • no need to handle side-effects • mutable
  14. Kea

  15. www.swingdev.io import { kea } from 'kea' export default kea({

    actions: () => ({ increment: (amount) => ({ amount }), decrement: (amount) => ({ amount }) }), reducers: ({ actions }) => ({ counter: [0, PropTypes.number, { [actions.increment]: (state, payload) => state + payload.amount, [actions.decrement]: (state, payload) => state - payload.amount }] }), selectors: ({ selectors }) => ({ doubleCounter: [ () => [selectors.counter], (counter) => counter * 2, PropTypes.number ] }) })
  16. www.swingdev.io import React from 'react' import logic from './logic' const

    Counter = (props) => ( <div> <button onClick={() => props.actions.increment(1)}>+1</button> <span>{props.counter}</span> <button onClick={() => props.actions.decrement(1)}>-1</button> </div> ) export default logic(Counter)
  17. • no explicit stores • difficult to make good project

    structure • not battle-tested Cons Pros • uses well-known Redux patterns • less boilerplate • perfect for small components
  18. React’s setState - master component class Gallery extends React.Component {

    state = { photos: [] }; loadPhotos() { axios.get('/some-url') .then((response) => { this.setState({ photos: response.data.photos.photo, }); }) .catch(() => {}); } renderPhoto = () => {}; render() { return ( <Fragment> {this.state.photos.map(this.renderPhoto)} </Fragment> ); } }
  19. Redux - actions everywhere // authentication actions export { SignUpRenter,

    SignUpHost, Login, Logout, ClearResetPassword, PushResetPasswordEmail, PushNewPassword, PushChangePassword, MakeGoogleAuth, MakeFacebookAuth, LoginAs }; // account actions export { FetchAccountDetails, FetchAllAccountDetails, FetchHostStripeConnectInfo, PushRequestedMoveInInfo, PushRenterContactInfo, PushPhoneNumber, PushRenterOnboardingInfo, PushRenterPaymentOptions, PushHostSpacePhotos, PushHostOtherInfo, PushRenterPaymentMethod, PushHostAddress, FetchRenterOnboardingInfo, PushAccountAvatar, PushLogMessage, CompleteOnboarding };
  20. MobX - complex stores import { observable } from 'mobx';

    class Comments { @observable collection = []; constructor() { this.load(); } load() { // load comments } create(body) { // return create Promise } update(id, body) { // return update Promise } delete(id) { // return delete Promise } }
  21. MobX - state access import { observable, observe } from

    'mobx'; const person = observable({ firstName: 'Maarten', lastName: 'Luther' }); const disposer = observe(person, (change) => { console.log( change.type, change.name, 'from', change.oldValue, 'to', change.object[change.name] ); }); person.firstName = 'Martin'; // dispose listener disposer();
  22. Kea - complex logic export default kea({ actions: () =>

    ({ start: true, finish: true, setCounter: (counter) => ({ counter }) }), reducers: ({ actions, key, props }) => ({ counter: [0, PropTypes.number, { [actions.setCounter]: (_, payload) => payload.counter }], finished: [false, PropTypes.bool, { [actions.start]: () => false, [actions.finish]: () => true }] }), takeLatest: ({ actions, workers }) => ({ [actions.start]: function* () { try { const { setCounter, finish } = this.actions for (let i = 50; i >= 0; i--) { yield put(setCounter(i)) } yield put(finish()) } finally { if (yield cancelled()) { console.log('Countdown was cancelled!') } } } }) });
  23. www.swingdev.io const ThemedButton = props => ( <ThemeContext.Consumer> {theme =>

    ( <button {...props} style={{ backgroundColor: theme.background, color: theme.foreground }} /> )} </ThemeContext.Consumer> ); const Toolbar = props => ( <ThemedButton onClick={props.changeTheme}> Change Theme </ThemedButton> ); const themes = { light: { foreground: '#000000', background: '#eeeeee' }, dark: { foreground: '#ffffff', background: '#222222' } }; const ThemeContext = React.createContext( themes.dark // default value );
  24. www.swingdev.io class App extends React.Component { state = { theme:

    themes.light }; toggleTheme = () => { this.setState(state => ({ theme: state.theme === themes.dark ? themes.light : themes.dark })); }; render() { // The ThemedButton button inside the ThemeProvider // uses the theme from state while the one outside uses // the default dark theme return ( <Fragment> <ThemeContext.Provider value={this.state.theme}> <Toolbar changeTheme={this.toggleTheme} /> </ThemeContext.Provider> <ThemedButton>Default Theme</ThemedButton> </Fragment> ); } }
  25. Features • allows to share data between components • supports

    default value • Provider and Consumer components • works with setState • works well with HOC pattern
  26. Further reading • Who needs state management anyways? • Application

    State Management • You are managing state? Think twice. • How We Ditched Redux for MobX • The Rise Of The State Machines • Kea vs setState, Redux, Mobx, Dva, JumpState, Apollo, etc.