M3 Tech meetup! #2 ~フロントエンドの副作用~ http://m3-engineer.connpass.com/event/33802/
ReduxMiddleware Wars
View Slide
Me• So$ware Engineer at M3, Inc.• ҩྍݱͰ͏γεςϜͷ։ൃ• AngularJS, Rails, Node.js, Babel, etc.• GitHub, Qiita: @shuhei• TwiGer: @shuheikagawa• ! Ϗʔϧݕఆ 2 ڃ
Middleware ʹ͍ͭͯͦ͢ͷલʹɾɾɾ
ͦͦ Redux ͱʁ• Predictable state container for JavaScript apps• ༧ଌՄೳͳঢ়ଶίϯςφʢ༁ʣ• ͱͱ Time traveling, hot reloading Λతͱͯ͠࡞ΒΕͨFlux తͳԿ͔
Redux ͷ 3 ݪଇ1. ΞϓϦͷશঢ়ଶΛҰͭͷ Object(State) ͱͯ͠อ࣋2. Action ΛૹΔʢdispatchʣ͜ͱʹΑͬͯͷΈ State ΛมߋͰ͖Δʢͱ͍͏͔৽͍͠ State ͕࡞ΒΕΔʣ3. Reducer ͱ͍͏ pure ͳؔʢಉ͡Ҿʹରͯ͠ৗʹಉ͡ฦΓΛฦ͢ʣʹΑͬͯͷΈঢ়ଶ͕มߋ͞ΕΔreducer : (State, Action) -> State
! Pure ͳؔͱʁ• ҾͷΈʹΑͬͯฦΓ͕ܾ·Δ• ؔͷ֎ͷঢ়ଶΛมߋ͠ͳ͍ʢ෭࡞༻͕ͳ͍ʣ
! Pure ͡Όͳ͍ؔ• ࣌ʹΑͬͯฦ͕͢ҧ͏• ฦΓ͕ͳ͍• ҾΛมߋͯ͠͠·͏• ޙͰίʔϧόοΫ͕ݺΕΔ• etc.
Β͵ؒʹΞϓϦͷঢ়ଶ͕มΘΔ͜ͱ͕ͳ͍
ͱ͍͑ɺ෭࡞༻Ͳ͏͢Δͷʁ• AJAX• local storage• cookie• ϩά• etc.
• Reducer ͷதʹॻ͘: ແཧ• View Component ͷதʹॻ͘: ͔ͤͬ͘ঢ়ଶΛ͍ग़ͨ͠ͷʹɾɾɾ• Ac4on Creator ͷதʹॻ͘: dispatch Λ͢ඞཁ͋Γ
Middleware
Redux Middleware ͱ• dispatch ͷલޙʹॲཧΛڬΉ• Action Λड͚ͯɺΰχϣΰχϣͰ͖Δ• Action ΛͪΐͬͱΒͤͯૹΔ• ผͷ Action ʹஔ͖͑Δ• ඇಉظॲཧΛߦ͍ɺ݁ՌΛ Actionͱͯ͠ૹΔ• e.g. WSGI, Rack, Express, Koa, etc.
ྫ: ͳʹ͠ͳ͍ड͚औͬͨ Ac$on Λͦͷ··࣍ͷ middleware ʹ͛Δconst passthrough = store => next => action => {return next(action);};
ྫ: ϩάΛग़ྗ࣍ͷ middleware ʹ ac+on Λ͛ΔʢnextʣલޙͰϩάΛग़ྗ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;};
ྫ: 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);
ࠓճඇಉظॲཧΛߦ͏ Middleware ʹয
΄͍͠ͷ• ! ςετͷ͢͠͞ʢϞοΫແ͠ͰςετͰ͖Δͱ࠷ߴʣ• " ۭ࣌Λ͑Δʢthro'ling, debouncingʣ• # ϩδοΫͷڽू• ✨ ։ൃͷ׆ൃ͞
ඇಉظॲཧΛߦ͏ MiddlewareಠஅͱภݟʹΑΓ...| ----------------- | ---- | --------- | ---- | ---- | ---- | ---- || 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 || ----------------- | ---- | --------- | ---- | ---- | ---- | ---- |⭐ ͷ 2016/7/5 ௐ
redux-thunk ⭐2392Ac#on ͱͯؔ͠ʢthunkʣ͕དྷͨΒɺͦΕΛ࣮ߦ͢ΔʢҾʹdispatch ͱ getStateʣ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());
redux-thunk ⭐2392• ! ϞοΫ͠ͳ͍ͱςετ͕ॻ͚ͳ͍• " Ac%on ΛҰ͔ͭͮͭ͠ड͚ΒΕͳ͍ͷͰɺdebounce ͳͲ͕͍͠• # ΄Ͳ΄ͲʹෳࡶͳϩδοΫॻ͚Δ• ✨ ΄Ͳ΄Ͳʹϝϯς͞Ε͍ͯΔɻͦͦ 13 ߦ͔͠ͳ͍• ࢀߟ: How to dispatch a Redux ac%on with a %meout?
redux-promise ⭐864FSA (Flux Standard Ac/on) ͷ payload ͷ promise Λղܾ͠ɺAc/on ͱͯ͠ૹΓͯ͘͠ΕΔfunction fetchItems() {const promise = fetch('/items').then(res => res.json());return {type: 'FETCH_ITEMS',payload: promise};}dispatch(fetchItems());
redux-promise ⭐864• ! γϯϓϧͳͷͰɺͦ͜·Ͱςετ͠ͳͯ͘ྑͦ͞͏• " debounce ͳͲͰ͖ͳ͍• # ෳࡶͳϩδοΫॻ͚ͳ͍• ✨ 4 ϲ݄ఔϝϯς͞Εͯͳ͍ɻ25 ߦ͚͕ͩͩɾɾɾ• Τϥʔॲཧͷൺֱతॏཁͦ͏ͳ MR ͕Ϛʔδ͞Ε͕ͨϦϦʔε͞Ε͍ͯͳ͍
redux-saga ⭐3172Saga ͱݺΕΔ generator (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);}
redux-saga ⭐3172• ! ςετ͕͍͢͠ʂʂʂ• ෭࡞༻ϦΫΤετΛ yield ͯ͠ɺmiddleware ʹ࣮ߦͯ͠Β͍ɺ݁ՌΛड͚औΔ• ࣗ pure ͳ··ෳࡶͳඇಉظॲཧ͕ॻ͚Δ• ৄ͘͠ @kuy ͞Μͷ͓Ͱɾɾɾ
redux-loop (store enhancer) ⭐641Elm Λਅࣅͨ APIɻreducer ͕ state ͚ͩͰͳ͘ Effects ฦ͢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;}}
redux-loop (store enhancer) ⭐641• ! ϞοΫͳ͠ͰςετͰ͖Δ• Effects ͨͩͷ Objectɻ࣮ߦ͢Δ·Ͱ෭࡞༻ى͖ͳ͍• " debounce ͳͲͰ͖ͳͦ͞͏• # Effects ΛΈ߹ΘͤΔ͜ͱ͕Ͱ͖Δ• ✨ 3 ϲ݄΄Ͳϝϯς͞Ε͍ͯͳ͍ɻ࡞ऀ͕ Elm ͔͠ॻ͔ͳ͘ͳͬͯ͠·ͬͨʁ
redux-observable ⭐ 634reducer ͷޙͰ Action Λड͚ͯ Action Λग़ྗ͢Δ ObservableΛฦ͢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$);}
redux-observable ⭐ 634• ! ϞοΫ͠ͳ͍ͱςετͰ͖ͳ͍• " Rx ͷڧྗͳ operator Ͱ༰қʹۭ࣌Λ੍ͤΔ• # redux-saga తͳ background processor style ʹਐԽ• ෳࡶͳॲཧॻ͚Δ• ✨ ݱࡏ։ൃதɻͱͯγϯϓϧ• Rx ΩϚΔͱؾ͍͍࣋ͪʢݸਓͷײʣ
τϨϯυʢʁʣ• σʔλͱͯ͠ͷ෭࡞༻ʢsaga, loop, Cycle.jsʣ• ෭࡞༻ϦΫΤετΛ middleware ͕ॲཧ͢Δ• ։ൃऀ͕ॻ͘ϩδοΫ͕៉ྷʹอͯΔ• Background processor ελΠϧʢsaga, observable, chooʣ• reducer ͷલͰͳ͘ޙͰ Action Λड͚Δ
࠙ձͰ͍Ζ͍Ζڭ͑ͯԼ͍͞ʂ
Thanks!
Credits• Chart of Redux from h0p://www.bebe0erdeveloper.com/coding/ge=ng-started-react-redux.html