Redux Middleware Wars (English)

Redux Middleware Wars (English)

My talk at M3 Tech meetup! #2 -Front-end side effects-
http://m3-engineer.connpass.com/event/33802/

Ca0ab6e450f894e06652ee257df9d647?s=128

Shuhei Kagawa

July 14, 2016
Tweet

Transcript

  1. Redux Middleware Wars

  2. Me • So$ware Engineer at M3, Inc. • Developing medical

    apps • AngularJS, Rails, Node.js, Babel, etc. • GitHub, Qiita: @shuhei • TwiKer: @shuheikagawa • ! 2nd-grade Beer CerPficate
  3. Before star*ng to talk about Redux middlewares...

  4. What's Redux at all? • Predictable state container for JavaScript

    apps • Something like Flux that was originally created for >me traveling and hot reloading
  5. 3 principles of Redux 1. Express all the app state

    as a single Object (State) 2. You can update State only by dispatching Action, or rather a new State is created 3. State is updated by pure func)ons called Reducer, which always returns the same result for each set of arguments reducer : (State, Action) -> State
  6. ! Pure func)on • Idempotent: Return value is calculated only

    by arguments • No side-effect: Does not change states outside of the func>on
  7. ! Impure func+on • Returns different values from 3me to

    3me • Returns nothing • Mutates passed arguments • Calls a callback func3on later • etc.
  8. You won't overlook state change

  9. None
  10. So, what about side effects? • AJAX • local storage

    • cookie • logging • etc.
  11. • Do in Reducer: Please don't • Do in view

    component: We want to keep components as simple as possible • Do in Ac<on Creator: You have to pass dispatch only to effecBul ac<on creators, which is kind of inconsistent
  12. Middleware

  13. What's Redux Middleware • Do something befor/a2er dispatch call •

    You can receive an Action and do something with it: • Delay Action • Replace an Action to another one • Do asynchronous thing and dispatch the result as an Action • e.g. WSGI, Rack, Express, Koa, etc.
  14. Example 1: Do nothing Pass through the received ac0on to

    the next middleware const passthrough = store => next => action => { return next(action); };
  15. Example 2: Logging Log before and a,er dispatching an ac3on

    (next()) to the next middleware const logger = store => next => action => { console.log('prev state', store.getState()); console.log('action', action); const returnValue = next(action); console.log('next state', store.getState()); return returnValue; };
  16. Example 3: Wait for the result of a Promise const

    promise = store => next => action => { if (typeof action.then === 'function') { return action.then(store.dispatch); } else { return next(action); } }; function fetchPosts() { return fetch('/posts').then(res => res.json()) .then(posts => ({ type: 'POSTS_FETCHED', posts })); } dispatch(fetchPosts);
  17. This talk focuses on middlewares that handle general-purpose asynchronous procedures

  18. What I want • ! Testability without mocking • "

    Control 7ming (thro9ling, debouncing) • # Cohesion of complex logic (wizard, etc.) • ✨ Ac7veness of development
  19. Middlewares for asynchronous handling From my biased perspec/ve... | -----------------

    | ---- | --------- | ---- | ---- | ---- | ---- | | name | star | from | test | time | cohe | acti | | ----------------- | ---- | --------- | ---- | ---- | ---- | ---- | | redux-thunk | 2392 | '15/07/13 | C | C | B | A | | redux-promise | 864 | '15/07/02 | - | C | C | C | | redux-saga | 3172 | '15/11/30 | B | A | A | A | | redux-loop | 641 | '16/01/06 | A | C | B | C | | redux-observable | 634 | '16/02/16 | C | A | B | A | | ----------------- | ---- | --------- | ---- | ---- | ---- | ---- | The numbers of ⭐ were recorded on June 5th, 2016
  20. redux-thunk ⭐2392 Executes the func,on with dispatch and getState as

    arguments if an Action is a func,on (thunk) const fetchBeers = () => (dispatch, getState) => { return fetch('/beers') .then(res => res.json()) .then(items => dispatch('FETCH_BEERS_SUCCEEDED', items)) .catch(error => dispatch('FETCH_BEERS_FAILED', error)); }; dispatch(fetchBeers());
  21. redux-thunk ⭐2392 • ! Requires mocking for tes2ng • "

    Hard to do debouncing because it takes ac2ons one by one • # Able to describe complex logic • ✨ Well maintained (actually only 13 lines of code!) • Reference: How to dispatch a Redux ac2on with a 2meout?
  22. redux-promise ⭐864 Resolves a Promise payload of a FSA, Flux

    Standard Ac7on and dispatches the result function fetchItems() { const promise = fetch('/items').then(res => res.json()); return { type: 'FETCH_ITEMS', payload: promise }; } dispatch(fetchItems());
  23. redux-promise ⭐864 • ! Too simple to test • "

    Can't do debouncing and etc. • # Too simple for complex logic • ✨ No commit in the last 4 months (only 25 lines of code though...) • A seemingly important MR for error handling has been merged but not released
  24. redux-saga ⭐3172 Describes opera,ons with side effects in generator called

    saga function* fetchBeer(action) { try { const beers = yield call(fetchBeer, action.id); yield put({ type: 'FETCH_BEER_SUCCEEDED', payload: beers }); } catch (error) { yield put({ type: 'FETCH_BEER_FAILED', error }); } } function* watchFetchBeer() { yield* takeEvery('FETCH_BEER', fetchBeer); }
  25. redux-saga ⭐3172 • ! Super easy to test!!! • yields

    side effect requests, has the middleware to execute them, and receives the response • Able to describe complex asynchronous opera=ons while keeping them pure • See the upcoming talk by @kuy
  26. redux-loop (store enhancer) ⭐641 API designed a,er Elm. reducer returns

    Effects in addi4on to state function beers(state = [], action) { switch (action.type) { case 'FETCH_BEERS': return loop(state, Effects.promise(fetchBeers)); case 'FETCH_BEERS_SUCCEEDED': return action.payload; default: return state; } }
  27. redux-loop (store enhancer) ⭐641 • ! Mock-free tes-ng • Effects

    are plain Objects. Side effects won't happen un-l executed • " hard to do debouncing and etc. • # Composable Effects • ✨ Not maintained for 3 months. The author has completely moved to the Elm world?
  28. redux-observable ⭐ 634 Receives an Observable<Action> and returns an Observable<Action>

    of addi0onal Actions function beerEpic(action$, store) { const beers$ = action$.ofType('FETCH_ITEMS') .mergeMap(() => Observable.fromPromise(fetchBeers())) .map(beers => ({ type: 'FETCH_BEERS_SUCCEEDED', payload: beers })); return Observable.merge( beers$, something$ ); }
  29. redux-observable ⭐ 634 • ! Needs mocking to test epics

    • " Able to control 4me by Rx's powerful operators • # Progressed into background processor style like redux-saga • Able to express complex procedure • ✨ Being ac4vely developed and super simple source code • Rx is fun to use!
  30. Trend (?) • Side effects as data (redux-saga, redux-loop, Cycle.js)

    • Middleware handles side effect requests • App logic is kept clean • Background processor style (redux-saga, redux-observable, choo) • receives Actions a"er reducer instead of before reducer
  31. Thanks!

  32. Credits • Chart of Redux from h0p:/ /www.bebe0erdeveloper.com/coding/ ge=ng-started-react-redux.html