redux-saga
by Elizaveta Anatskaya
Redux is synchronous
Redux manages your application state synchronously.
One of the main concepts of Redux is reducers.
The reducer is a pure function that takes the previous state and an action, and returns the next state.
It’s very important that the reducer stays pure. Things you should never do inside a reducer:
middleware provides a third-party extension point between dispatching an action, and the moment it reaches the reducer
Middlewares don’t come out of the box with redux. It is usually a package that we will install, or we can write one for our selves.
“Sagas are implemented as generator functions that yield objects to the redux-saga middleware.”
— is a library that aims to make application side effects easier to manage (i.e. asynchronous things like data fetching and impure things like accessing the browser cache)
— is a redux middleware, which means this thread can be started, paused and cancelled from the main application with normal redux actions, it has access to the full redux application state and it can dispatch redux actions as well.
export function* helloSaga() {
console.log('Hello Sagas!')
}
//define
function* generator(i) {
yield i;
yield i + 10;
}
//init
const gen = generator(10);
//use
console.log(gen.next()); // {value: 10, done: false}
console.log(gen.next()); // {value: 20, done: false}
console.log(gen.next()); // {value: undefined, done: true}
// define
function* generator() {
const i = 1000;
yield i;
const result = yield i + 1;
yield i + result;
}
// init
const gen = generator();
// use
console.log(gen.next()); // {value: 1000, done: false}
console.log(gen.next()); // {value: 1001, done: false}
console.log(gen.next(3)); // {value: 1003, done: false}
console.log(gen.next()); // {value: undefined, done: true}
👈 Wait. What?? 🤔
Here is why: calling the next() method with an argument will resume the generator function execution, replacing the yield expression where execution paused with the argument from next().
On the third time, we call next() the returned object is {value: 1003, done: false} because i equals to 1000 and then we pass the value 3 to the next method, hence 1000 + 3 yielded 1003.
...so now that we’ve learned how to use Generator, we are ready to learn about Redux-Saga! 💪
import { createStore, applyMiddleware } from 'redux';
import createSagaMiddleware from 'redux-saga';
import reducer from './reducers';
import rootSaga from './saga';
// create the saga middleware
export const sagaMiddleware = createSagaMiddleware();
// mount it on the store
const store = createStore(
reducer,
applyMiddleware(sagaMiddleware)
);
// run the saga
sagaMiddleware.run(rootSaga);
When we create the Redux store, we also create our Redux-Saga middleware and connect it to the store via applyMiddleware. After the store was created, we call run with our root saga, which starts our redux-saga middleware.
// watcher saga
export function* watchGetItems() {
yield takeEvery('GET_ITEMS_REQUEST_ACTION', getItems);
}
// worker saga
export function* getItems() {
const items = yield call(api.getItems);
yield put({
type: 'GET_ITEMS_SUCCESS_ACTION',
payload: items
});
}
// watcher saga
export function* watchGetItems() {
yield takeEvery('GET_ITEMS_REQUEST_ACTION', getItems);
}
// worker saga
export function* getItems() {
const items = yield call(api.getItems);
yield put({
type: 'GET_ITEMS_SUCCESS_ACTION',
payload: items
});
}
In the above example, the watcher saga is listening to GET_ITEMS_REQUEST_ACTION. When this action type is dispatched, the watcher calls getItems saga. Then, Redux-Saga will start executing getItems function. On the first yield, we call some API, and on the second yield, we dispatch another action of type GET_ITEMS_SUCCESS_ACTION and payload containing the result of the previous yield.
call and put are effect creators.
are redux actions which serve as instructions for Saga middleware
export function* watcherSaga() {
yield takeEvery('SOME_ACTION', workerSaga);
}
export function* watcherSaga() {
yield takeLatest('SOME_ACTION', workerSaga);
}