Using Saga Helpers

redux-saga provides some helper functions to spawn tasks when some specific actions are dispatched to the Store.

The helper functions are built on top of the lower level API. In the advanced section, we'll see how those functions can be implemented.

The first function, takeEvery is the most familiar and provides a behavior similar to redux-thunk.

Let's illustrate with the common AJAX example. On each click on a Fetch button we dispatch a FETCH_REQUESTED action. We want to handle this action by launching a task that will fetch some data from the server.

First we create the task that will perform the asynchronous action:

import { call, put } from 'redux-saga/effects'

export function* fetchData(action) {
   try {
      const data = yield call(Api.fetchUser, action.payload.url);
      yield put({type: "FETCH_SUCCEEDED", data});
   } catch (error) {
      yield put({type: "FETCH_FAILED", error});
   }
}

To launch the above task on each FETCH_REQUESTED action:

import { takeEvery } from 'redux-saga'

function* watchFetchData() {
  yield* takeEvery('FETCH_REQUESTED', fetchData)
}

In the above example, takeEvery allows multiple fetchData instances to be started concurrently. At a given moment, we can start a new fetchData task while there are still one or more previous fetchData which have not yet terminated.

If we want to only get the response of the latest request fired (e.g. to display always the latest version of data) we can use the takeLatest helper:

import { takeLatest } from 'redux-saga'

function* watchFetchData() {
  yield* takeLatest('FETCH_REQUESTED', fetchData)
}

Unlike takeEvery, takeLatest allows only one fetchData task to run at any moment. And it's the latest started task. If a previous task is still running, it'll be automatically cancelled.

If you have multiple Sagas watching for different actions. You can create multiple watchers and fork them (We'll see about fork later. For now consider it's an Effect that allows us to start multiple sagas on the background)

For example

import { takeEvery } from 'redux-saga'
import { fork } from 'redux-saga/effects'

// FETCH_USERS
function* fetchUsers(action) { ... }

function* watchFetchUsers() {
  yield* takeEvery('FETCH_USERS', fetchUsers)
}

// CREATE_USER
function* createUser(action) { ... }

function* watchCreateUser() {
  yield* takeEvery('CREATE_USER', createUser)
}

// user fork to start the 2 watchers in parallel
export default function* rootSaga() {
  yield fork(watchFetchUsers)
  yield fork(watchCreateUser)
}

Alternatively you can use this shortcut form.

import { takeEvery } from 'redux-saga'
import { fork } from 'redux-saga/effects'

function* fetchUsers(action) { ... }
function* createUser(action) { ... }

// will start takeEvery in the background and provide it with the subsequen arguments
export default function* rootSaga() {
  yield fork(takeEvery, 'FETCH_USERS', fetchUsers)
  yield fork(takeEvery, 'CREATE_USER', createUser)
}