Slide 1

Slide 1 text

LET’S FIGHT REDUX SIDE-EFFECTS SHOWDOWN ANKITA KULKARNI, RANGLE.IO @KULKARNIANKITA9

Slide 2

Slide 2 text

REDUX IS THE PREFERRED 
 STATE-MANAGEMENT LIBRARY

Slide 3

Slide 3 text

WHAT ARE 
 SIDE-EFFECTS?

Slide 4

Slide 4 text

POPULARITY ⭐ REDUX THUNKS ⭐~11K REDUX OBSERVABLES ⭐~6K REDUX 
 SAGAS ⭐~16K

Slide 5

Slide 5 text

LET’S LOOK AT LOGIN

Slide 6

Slide 6 text

AT FIRST, 
 LOGIN IS SIMPLE

Slide 7

Slide 7 text

No content

Slide 8

Slide 8 text

AT FIRST, LOGIN IS SIMPLE LOGIN SUCCESS LOGIN 
 FAILURE LOGIN REQUEST ActionType.LOGIN_REQUEST ActionType.LOGIN_FAILURE ActionType.LOGIN_SUCCESS

Slide 9

Slide 9 text

DECLARING AN ACTION const loginRequest = (username, password) => ({ type: ActionType.LOGIN_REQUEST, payload: { username, password } }); const loginSuccess = user => ({ type: ActionType.LOGIN_SUCCESS, payload: user, }); const loginFailure = err => ({ type: ActionType.LOGIN_FAILURE, payload: err, error: true, });

Slide 10

Slide 10 text

LISTEN TO THOSE ACTIONS IN REDUCER // Reducer that takes in initialState and action function login(state = initialState, action) { switch (action.type) { case ActionType.LOGIN_SUCCESS: return { user: action.payload.user } case ActionType.LOGIN_FAILURE: return { err: action.payload.err, user: null} default: // always return the state if something were to go wrong! return state } }

Slide 11

Slide 11 text

No content

Slide 12

Slide 12 text

THINGS GET REALLY INTERESTING WITH ASYNC… LOGIN SUCCESS LOGIN 
 FAILURE ASYNC 
 REQUEST LOGIN REQUEST (

Slide 13

Slide 13 text

No content

Slide 14

Slide 14 text

REDUX THUNKS PROMISES REDUX 
 SAGAS WORKER + WATCHER REDUX OBSERVABLES EPIC (TYPE + OPERATORS) MENTAL MODEL

Slide 15

Slide 15 text

EXAMPLE PLEASE…

Slide 16

Slide 16 text

THUNKS const loginThunk = (username, password) => dispatch => { dispatch(loginRequest(username, password)); return function (dispatch) { return loginUserApiCall(username, password).then(user => { dispatch(loginSuccess(user)); }, err => dispatch(loginFailure(err)) ); }; }

Slide 17

Slide 17 text

THUNKS const loginThunk = (username, password) => dispatch => { dispatch(loginRequest(username, password)); return function (dispatch) { return loginUserApiCall(username, password).then(user => { dispatch(loginSuccess(user)); }, err => dispatch(loginFailure(err)) ); }; }

Slide 18

Slide 18 text

THUNKS const loginThunk = (username, password) => dispatch => { dispatch(loginRequest(username, password)); return function (dispatch) { return loginUserApiCall(username, password).then(user => { dispatch(loginSuccess(user)); }, err => dispatch(loginFailure(err)) ); }; }

Slide 19

Slide 19 text

SAGAS import { call, put, take } from 'redux-saga/effects'; function* loginUserWorker(action) { try { const { username, password } = action.payload; const user = yield call(loginUserApiCall, username, password); yield put(loginSuccess(user)); } catch (e) { yield put(loginFailure(e)); } } function* loginUserWatcher() { yield take(ActionType.LOGIN_REQUEST, loginUserWorker); }

Slide 20

Slide 20 text

OBSERVABLES import { of } from "rxjs"; import { mergeMap, mapTo, map } from "rxjs/operators"; const loginRequestEpic = action$ => action$.pipe( ofType(ActionType.LOGIN_REQUEST), mergeMap(action => { const { username, password } = action.payload; return loginUserApiCall(username, password) }), mergeMap(res => of(loginSuccess(res))) catchErr(err => of(loginFailure(err))) );

Slide 21

Slide 21 text

OBSERVABLES import { of } from "rxjs"; import { mergeMap, mapTo, map } from "rxjs/operators"; const loginRequestEpic = action$ => action$.pipe( ofType(ActionType.LOGIN_REQUEST), mergeMap(action => { const { username, password } = action.payload; return loginUserApiCall(username, password) }), mergeMap(res => of(loginSuccess(res))) catchErr(err => of(loginFailure(err))) );

Slide 22

Slide 22 text

OBSERVABLES import { of } from "rxjs"; import { mergeMap, mapTo, map } from "rxjs/operators"; const loginRequestEpic = action$ => action$.pipe( ofType(ActionType.LOGIN_REQUEST), mergeMap(action => { const { username, password } = action.payload; return loginUserApiCall(username, password) }), mergeMap(res => of(loginSuccess(res))) catchErr(err => of(loginFailure(err))) );

Slide 23

Slide 23 text

REAL LIFE IS HARD…

Slide 24

Slide 24 text

THIS IS HOW IT ENDS UP LOOKING LIKE… LOGIN SUCCESS LOGIN 
 FAILURE ASYNC 
 REQUEST LOGIN REQUEST ( LOG ANALYTICS LOAD DATA FOR DASHBOARD CHECK ACCESS CONTROL LOAD USER PROFILE DO MORE STUFF…

Slide 25

Slide 25 text

USERS ARE VERY NOTORIOUS

Slide 26

Slide 26 text

LET’S LOOK AT A COMPLEX EXAMPLE

Slide 27

Slide 27 text

No content

Slide 28

Slide 28 text

CANCELING REQUESTS

Slide 29

Slide 29 text

THUNKS It is very hard to cancel requests in thunks

Slide 30

Slide 30 text

SAGAS import { call, put, fork, cancel, takeEvery } from 'redux-saga/effects'; function* watchMovieWorker(action) { try { const { query } = action.payload; const user = yield call(loadMovieApiCall, query); yield put(watchMovieSuccess(user)); } catch (e) { yield put(watchMovieFailure(e)); } } function* watchMovieWatcher() { while (yield take(ActionType.WATCH_MOVIE_REQUEST)){ const bgWatchMovieTask = yield fork(watchMovieWorker); yield take(ActionType.WATCH_MOVIE_CANCELLED); yield cancel(bgWatchMovieTask); } }

Slide 31

Slide 31 text

OBSERVABLES const watchMovieRequestEpic = (action$, state$) => action$.pipe( ofType(ActionType.WATCH_MOVIE_REQUEST), delay(2000), mergeMap(action => { const { query } = action.payload; return loadMovieApiCall(query); }), mergeMap(res => of(watchMovieSuccess(res))), takeUntil(action$.pipe(ofType(ActionType.WATCH_MOVIE_CANCELLED))), catchError(err => { return of(watchMovieFailure(err)) }), repeat() );

Slide 32

Slide 32 text

DOING MANY THINGS AT ONCE

Slide 33

Slide 33 text

THIS IS JUST THE BEGINNING… LOG ANALYTICS LOAD MOVIES FOR DASHBOARD CHECK ACCESS CONTROL LOAD USER PROFILE DO MORE STUFF… LOGIN SUCCESS

Slide 34

Slide 34 text

LET’S FOCUS ON 2 LOG ANALYTICS LOAD MOVIES FOR DASHBOARD CHECK ACCESS CONTROL LOAD USER PROFILE DO MORE STUFF… LOGIN SUCCESS

Slide 35

Slide 35 text

THUNKS const netflixThunk = (username, password) => dispatch => { dispatch(loginRequest(username, password)); return function (dispatch) { return loginUserApiCall(username, password).then(user => { dispatch(loginSuccess(user)); return watchMovieApiCall().then(movie => { dispatch(watchMovieSuccess(movie)); return logAnalytics().then(data => { dispatch(logAnalyticsSuccess(data)); }, err => dispatch(logAnalyticsFailure(err)); }, err => dispatch(watchMovieFailure(err)); }, err => dispatch(loginFailure(err)) ); }; }

Slide 36

Slide 36 text

const netflixThunk = (username, password) => dispatch => { dispatch(loginRequest(username, password)); return function (dispatch) { return loginUserApiCall(username, password).then(user => { dispatch(loginSuccess(user)); return watchMoviesApiCall().then(movie => { dispatch(watchMovieSuccess(movie)); return logAnalytics().then(data => { dispatch(logAnalyticsSuccess(data)); }, err => dispatch(logAnalyticsFailure(err)); }, err => dispatch(watchMovieFailure(err)); }, err => dispatch(loginFailure(err)) ); }; }

Slide 37

Slide 37 text

SAGAS function* watchMovieWorker() { ... } function* logAnalyticsWorker() { ... } function* watcher() { const movies = yield* watchMovieWorker() yield put(ActionType.WATCH_MOVIE) const analytics = yield* logAnalyticsWorker() yield put(ActionType.LOG_ANALYTICS) }

Slide 38

Slide 38 text

OBSERVABLES const logAnalyticsEpic = action$ => action$ .ofType(ActionType.LOGIN_SUCCESS) .reduce(logAnalytics) .flatMap(events => Observable.merge( events, ActionType.LOG_ANALYTICS)); const watchMovieEpic = action$ => action$ .ofType(ActionType.LOGIN_SUCCESS) .reduce(watchMovie) .map(movies => ActionType.WATCH_MOVIES) LOGIN SUCCESS

Slide 39

Slide 39 text

SUMMARY Sagas: Pros: ‣ Got to dispatch a separate action to run ‣ ES6 generator knowledge ‣ Easy to test ‣ Has cancellation ‣ Flexible with saga effects - fork, spawn, cancel, throttle Cons: ‣ ES6 generator knowledge ‣ Can be harder to maintain if not done right ‣ As app scales, harder to do cancellation Thunks: Pros: ‣ Really easy to understand ‣ Can be used for smaller projects Cons: ‣ Return promises ‣ Promises cannot be cancelled ‣ Hard to test as you are testing functions ‣ Tightly coupled ‣ Hard to reuse ‣ is a function that returns a function ‣ Call back hell ‣ Too much nesting ‣ Code readability ‣ No way to cancel, debounce, retry Observables: Pros: ‣ You got to filter from a set of actions ‣ Rx js knowledge ‣ Can be transformed ‣ Can be re-used very easily ‣ Built-in debounce, throttle, ‣ Are lazy so you can retry, repeat ‣ Epic - actions in, actions out ‣ Are very powerful Cons: ‣ Testing is a bit more difficult than sagas ‣ RxJS has a huge learning curve ‣ Not all apps need that much power

Slide 40

Slide 40 text

No content

Slide 41

Slide 41 text

WINNER ✨ REDUX SAGAS
 ⭐

Slide 42

Slide 42 text

No content

Slide 43

Slide 43 text

THANK YOU @KULKARNIANKITA9