Managing async flow in js

AGENDA

  • Redux
  • Redux Thunk
  • Redux Saga
  • References

Redux

Redux is a predictable state container for JavaScript apps.

  1. Single source of truth (store)

  2. State is read-only (actions)

  3. Changes are made with pure functions (reducers)

Single source of truth (store)

store.getState()

// returns
{
    animalType: 'cat',
    pets: [{
        name: 'John Snow',
        age: 2,
        description: 'nya nya nya'
    }]
}

State is read only (actions)

store.dispatch({
    type: 'CHANGE_ANIMAL',
    animalType: 'dog'
});

Changes are made with pure functions (reducers)

const initialState = fromJS({
  animalType: 'cat',
  pets: []
});


function adoptionPageReducer(state = initialState, action) {
  switch (action.type) {
    case CHANGE_ANIMAL:
      return state.set('animalType', action.animalType);
    default:
      return state;
  }

Redux Thunk 

Redux Thunk middleware allows you to write action creators that return a function instead of an action. The thunk can be used to delay the dispatch of an action, or to dispatch only if a certain condition is met. ​

A thunk is a function that wraps an expression to delay its evaluation

// calculation of 1 + 2 is immediate
// x === 3
let x = 1 + 2;

// calculation of 1 + 2 is delayed
// foo can be called later to perform the calculation
// foo is a thunk!
let foo = () => 1 + 2;

Action Creator

// function that create actions

function changeAnimal(animalType) {
    return {
        type: 'CHANGE_ANIMAL',
        animalType
    }
}

// So I can do 
dispatch(changeAnimal('dog'));

// instead of 
dispatch({ type: 'CHANGE_ANIMAL', animalType: 'dog' });

Example

I want to change the animal filter when I click on submit button

SYNC

function changeAnimal(animalType) {
    return {
        type: 'CHANGE_ANIMAL',
        animalType
    };
}
<button onClick={() => dispatch(changeAnimal('dog'))}> 
    Dog 
</button>

HTML

JS

ASYNC without Thunk

function asyncChangeAnimal(dispatch, animalType) {
    checkIfAnimalTypeIsAvailable
        .then(() => dispatch(changeAnimal(animalType))
        .catch((e) => dispatch(errorWrapper(e))
<div onClick={() => asyncChangeAnimal(this.props.dispatch, 'dog')}> 
    Dog 
</div>

HTML

JS

ASYNC with Thunk (IoC)

function asyncChangeAnimal(animalType) {
    return dispatch => checkIfAnimalTypeIsAvailable
        .then(() => dispatch(changeAnimal(animalType))
        .catch((e) => dispatch(errorWrapper(e))
}
<button onClick={() => dispatch(asyncChangeAnimal('dog'))}> 
    Dog 
</button>

HTML

JS

Redux Saga

A library that aims to make side effects (i.e. asynchronous things like data fetching and impure things like accessing the browser cache) in React/Redux applications easier and better.

Underlying Concepts

  • CQRS & Event Sourcing
  • Observables & Generators
  • Actions, Events, Commands
  • Process Manager Pattern
  • Event Streams

Generators

Functions that don't "run-to-complete"

function *myGenerator() {
    yield 'first yield';
    yield 'second yield';
    return 'hi';
}

let iterator = myGenerator();

console.log(iterator.next()); 
// { value: 'first yield', done: false }

console.log(iterator.next()); 
// { value: 'second yield', done: false }

console.log(iterator.next());
// { value: 'hi', done: true }

console.log(iterator.next());
// { value: undefined, done: true }

Saga Example

function* fetchPets(action) {
   try {
      const user = yield call(PetsApi.fetch, action.animalType);
      yield put({ type: 'PETS_LOADED', pets });
   } catch (e) {
      yield put({ type: 'PETS_LOAD_ERROR', message: e.message });
   }
}


function* mySaga() {
    yield takeEvery("LOAD_PETS", fetchPets);
}

Code Examples

  • Counter
  • Shopping Cart
  • Pet Adoption

References

redux-saga

By Douglas Nomizo

redux-saga

  • 776