The evolution of redux action creators

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

Transcript of The evolution of redux action creators

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