What is Redux-Saga?

Redux-Saga is a middleware library used to allow a Redux store to asynchronously interact with resources outside of itself.

Redux-Saga helps in:

  • making HTTP requests
  • accessing browser storage
  • executing I/O operations

These types of operations are known as side-effects in Redux-Saga.

A saga is written as a function and implemented as a generator function that yields an object to saga middleware.

Code

Integration with the Redux store

You need to use the Redux-Saga middleware to connect to the Redux store.

import { createStore, applyMiddleware } from 'redux';
import createSagaMiddleware from 'redux-saga';

import reducer from './reducers';
import helloSaga from './sagas';

// 1. Create the saga middleware
const sagaMiddleware = createSagaMiddleware();
// 2. Pass it as a middleware while creating the store
const store = createStore(
  reducer,
  applyMiddleware(sagaMiddleware)
);

// Then run the saga
sagaMiddleware.run(helloSaga);

Examples

// sagas.js

// A `hello world` example:
export function* helloSaga() {
  console.log("Hello Sagas!");
}

// Async example:
// the asyncSaga Saga sleeps for 1 second via the call to timeout(1000), then dispatches an INCREMENT action(via `put` effect).
export function* asyncSaga() {
  yield timeout(1000);	// setTimeout is used to implement timeout
  yield put({ type: 'INCREMENT' });
}

In this example, we define two sagas (functions). One of them is a very simple HelloWorld program, and the other is an async saga.

In the async saga, we write the following:

yield timeout(1000)

It is a simple statement that calls an async function (setTimeout in this case), and execution is suspended until it resolves.

After that, control goes to the next statement:

yield put({ type: 'INCREMENT' })

This statement yields an object to the saga middleware and tells it to dispatch an INCREMENT action.

put is the magical word here, which is also known as an effect in redux-saga.

Effects

Effects are plain JavaScript objects that contain instructions to be fulfilled by the middleware.

They can be imported from redux-saga/effects.

There are various types of effects:

  1. put is one example of what we call an effect, which dispatches an action to the store.
put({type: 'INCREMENT'}) // => { PUT: {type: 'INCREMENT'} }
  1. call will call the given function.
call(timeout, 1000) // => { CALL: {fn: timeout,  args: [1000]}}

You could also call the timeout function normally (timeout(1000)) without using the call effect, but then you won’t be able to write the unit test cases for your saga properly.

Apart from the effects, there is a helper function that is required to write a basic saga.

Helper function

  1. takeEvery, a helper function provided by Redux-Saga, listens for dispatched actions (SOME_ACTION) and runs takeSomeAction each time.
function* watchSomeAction() {
	yield takeEvery('SOME_ACTION', takeSomeAction)
}

takeEvery is a way to tell Redux-Saga that you need to listen to this action and, whenever that is dispatched from a component, call takeSomeAction.

Below is a flow diagram for a basic Redux-Saga flow.

Basic flow of redux-saga
Basic flow of redux-saga

According to this diagram:

  1. A react component will dispatch some action.
  2. Redux-Saga is already listening to that action via the takeEvery() helper function.
  3. A saga bind to that action will be called and execute an async process.
  4. Once the async request gets fulfilled, the control goes back to the next line in the saga, which can update the Redux state by calling any other reducer function.

Here is the link to the demo for you to play around with this example. Make sure to read the README.md file first.

Free Resources