Top Banner
THE EVOLUTION OF REDUX ACTION CREATORS GEORGE BUKHANOV @NothernEyes northerneyes
36

The evolution of redux action creators

Jan 13, 2017

Download

Software

George Bukhanov
Welcome message from author
This document is posted to help you gain knowledge. Please leave a comment to let me know what you think about it! Share it to your friends and learn new things together.
Transcript
Page 1: The evolution of redux action creators

THE EVOLUTION OF REDUX ACTION CREATORS

GEORGE BUKHANOV

@NothernEyesnortherneyes

Page 2: The evolution of redux action creators
Page 3: The evolution of redux action creators
Page 4: The evolution of redux action creators

Redux is a predictable state container for JavaScript apps.

Page 5: The evolution of redux action creators
Page 6: The evolution of redux action creators

Action is just a plain object { type: ADD_TODO, text: 'Build my first Redux app'}

Page 7: The evolution of redux action creators

Reducer is a pure function function todoApp(state = initialState, action) { switch (action.type) { case ADD_TODO: return Object.assign({}, state, { todos: [ ...state.todos, { text: action.text, completed: false } ] }) default: return state }}

Page 8: The evolution of redux action creators

WHAT IS ACTION CREATOR?

Page 9: The evolution of redux action creators

Action creator function addTodo(text) { return { type: ADD_TODO, text }}

Page 10: The evolution of redux action creators

WHAT ABOUT ASYNC ACTIONS?

Page 11: The evolution of redux action creators

It provides a third-party extension point between dispatchingan action, and the moment it reaches the reducer.

REDUX MIDDLEWARE

Page 12: The evolution of redux action creators

Redux-thunk export default function thunkMiddleware({ dispatch, getState }) { return next => action => { if (typeof action === 'function') { return action(dispatch, getState); }

return next(action); };}

Page 13: The evolution of redux action creators

Action creator with redux-thunk function increment() { return { type: INCREMENT_COUNTER };}

function incrementAsync() { return dispatch => { setTimeout(() => { // Yay! Can invoke sync or async actions with ̀dispatch̀ dispatch(increment()); }, 1000); };}

Page 14: The evolution of redux action creators

WHAT ABOUT TESTING?

Page 15: The evolution of redux action creators

OUR ACTION CREATORS ARE NOT PURE FUNCTIONS

Page 16: The evolution of redux action creators

THE ANSWER IS

DEPENDENCY INJECTION

Page 17: The evolution of redux action creators

Middleware with dependency injection export default function injectDependencies(dependencies) { return ({dispatch, getState}) => next => action => { if (typeof action !== 'function') return next(action);

return action({dispatch, getState, ...dependencies}); };}

Page 18: The evolution of redux action creators

Action creator with DI export function registration(data) { return ({dispatch, api, history, analytics, cookie}) => { dispatch({type: authConstants.REGISTRATION_PENDING});

return api.register(data).then(res => { updateAnalytics(analytics, res, true); saveUserCookie(res, cookie); analytics.track('Registration started');

dispatch({type: authConstants.REGISTRATION_SUCCESS}); const link = '/search'; history.push(link); }).catch(onError(authConstants.REGISTRATION_ERROR, dispatch)); };}

Page 19: The evolution of redux action creators

YEAH! THEY ARE PURE FUNCTIONS

Page 20: The evolution of redux action creators

BUT IT IS NOT ENOUGH, WHY?

Page 21: The evolution of redux action creators

Tests are still complecated

We have some mess in components

etc...

function() { updateSearchPage()({dispatch, getState: buildState(), api, cookie}); expect(dispatch.calledOnce).to.be.true; expect(calledWithActions( dispatch.getCall(0).args, APPLIED_FILTERS_CHANGED, GET_NOTICES_SUCCESS )).to.be.true;};

function onHandlePress () { this.props.dispatch({type: 'SHOW_WAITING_MODAL'}) this.props.dispatch(createRequest())}

The most elegant way to write complecated action creators

REDUX-SAGA

Page 22: The evolution of redux action creators

The most elegant way to write complecated action creators

Look at this beautiful code

export function* authFlow() { while(true) { yield take(USER_AUTH_CHECK); yield fork(authenticate);

const {user, token} = yield take(USER_AUTH_SUCCESS); Session.save(user, auth); yield put(redirectTo('/'));

const action = yield take(USER_SIGN_OUT); Session.clear(); yield put(redirectTo('/')); }}

Page 23: The evolution of redux action creators

HOW IT WORKS?

Page 24: The evolution of redux action creators

GENERATORS

Page 25: The evolution of redux action creators

Generators are Functions with bene�ts. function* idMaker(){ var index = 0; while(true) yield index++;}

var gen = idMaker();

console.log(gen.next().value); // 0console.log(gen.next().value); // 1console.log(gen.next().value); // 2

Page 26: The evolution of redux action creators

co - generator based control �ow var fn = co.wrap(function* (val) { return yield Promise.resolve(val);});

fn(true).then(function (val) {

});

Page 27: The evolution of redux action creators

LET'S GET BACK TO REDUX-SAGA

Page 28: The evolution of redux action creators

Simple example What happenshere?

select part of the statecall the api methodput an action

export function* checkout() { try { const cart = yield select(getCart); yield call(api.buyProducts, cart); yield put(actions.checkoutSuccess(cart)); } catch(error) { yield put(actions.checkoutFailure(error)); }}

Page 29: The evolution of redux action creators

Easy to test test('checkout Saga test', function (t) { const generator = checkout() let next = generator.next() t.deepEqual(next.value, select(getCart), "must select getCart" ) next = generator.next(cart) t.deepEqual(next.value, call(api.buyProducts, cart), "must call api.buyProducts(cart)" )

next = generator.next() t.deepEqual(next.value, put(actions.checkoutSuccess(cart)), "must yield actions.checkoutSuccess(cart)" ) t.end()})

Page 30: The evolution of redux action creators

SAGAS CAN BE DAEMONS

Page 31: The evolution of redux action creators

Endless loop, is listenertake export function* watchCheckout() { while(true) { yield take(actions.CHECKOUT_REQUEST) yield call(checkout) }}

Page 32: The evolution of redux action creators

Root Saga export default function* root() { yield [ fork(watchCheckout) ]}

Page 33: The evolution of redux action creators

REDUX-SAGA PATTERNS

Page 34: The evolution of redux action creators

can be useful to handle AJAX requests where wewant to only have the response to the latest request.

takeLatest

function* takeLatest(pattern, saga, ...args) { let lastTask while(true) { const action = yield take(pattern) if(lastTask) // cancel is no-op if the task has alerady terminated yield cancel(lastTask)

lastTask = yield fork(saga, ...args.concat(action)) }}

Page 35: The evolution of redux action creators

allows multiple saga tasks to be forkedconcurrently.

takeEvery

function* takeEvery(pattern, saga, ...args) { while (true) { const action = yield take(pattern) yield fork(saga, ...args.concat(action)) }}

Page 36: The evolution of redux action creators

FIN

@NothernEyesnortherneyes