Managing async flow in js
AGENDA
- Redux
- Redux Thunk
- Redux Saga
- References
Redux
Redux is a predictable state container for JavaScript apps.
-
Single source of truth (store)
-
State is read-only (actions)
-
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
- http://redux.js.org/
- https://github.cron/redux-thunk
- https://redux-saga.js.org
- https://stackoverflow.com/questions/34570758/why-do-we-need-middleware-for-async-flow-in-redux?noredirect=1&lq=1
- https://davidwalsh.name/es6-generators
- http://thejsguy.com/2016/10/15/a-practical-introduction-to-es6-generator-functions.html
- http://blog.isquaredsoftware.com/2017/01/idiomatic-redux-thoughts-on-thunks-sagas-abstraction-and-reusability/
- https://jaysoo.ca/2016/01/03/managing-processes-in-redux-using-sagas/
redux-saga
By Douglas Nomizo
redux-saga
- 776