Slide 1

Slide 1 text

Redux Middleware Wars

Slide 2

Slide 2 text

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

Slide 3

Slide 3 text

Before star*ng to talk about Redux middlewares...

Slide 4

Slide 4 text

What's Redux at all? • Predictable state container for JavaScript apps • Something like Flux that was originally created for >me traveling and hot reloading

Slide 5

Slide 5 text

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

Slide 6

Slide 6 text

! Pure func)on • Idempotent: Return value is calculated only by arguments • No side-effect: Does not change states outside of the func>on

Slide 7

Slide 7 text

! Impure func+on • Returns different values from 3me to 3me • Returns nothing • Mutates passed arguments • Calls a callback func3on later • etc.

Slide 8

Slide 8 text

You won't overlook state change

Slide 9

Slide 9 text

No content

Slide 10

Slide 10 text

So, what about side effects? • AJAX • local storage • cookie • logging • etc.

Slide 11

Slide 11 text

• Do in Reducer: Please don't • Do in view component: We want to keep components as simple as possible • Do in Ac

Slide 12

Slide 12 text

Middleware

Slide 13

Slide 13 text

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.

Slide 14

Slide 14 text

Example 1: Do nothing Pass through the received ac0on to the next middleware const passthrough = store => next => action => { return next(action); };

Slide 15

Slide 15 text

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; };

Slide 16

Slide 16 text

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);

Slide 17

Slide 17 text

This talk focuses on middlewares that handle general-purpose asynchronous procedures

Slide 18

Slide 18 text

What I want • ! Testability without mocking • " Control 7ming (thro9ling, debouncing) • # Cohesion of complex logic (wizard, etc.) • ✨ Ac7veness of development

Slide 19

Slide 19 text

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

Slide 20

Slide 20 text

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());

Slide 21

Slide 21 text

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?

Slide 22

Slide 22 text

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());

Slide 23

Slide 23 text

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

Slide 24

Slide 24 text

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); }

Slide 25

Slide 25 text

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

Slide 26

Slide 26 text

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; } }

Slide 27

Slide 27 text

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?

Slide 28

Slide 28 text

redux-observable ⭐ 634 Receives an Observable and returns an Observable 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$ ); }

Slide 29

Slide 29 text

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!

Slide 30

Slide 30 text

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

Slide 31

Slide 31 text

Thanks!

Slide 32

Slide 32 text

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