Saga Http Requests
In a worker saga, yield replaces await and put replaces dispatch. Use both to implement the pending/fulfilled/rejected HTTP pattern.
The worker saga is connected and fires on FETCH_TASKS. Now for the async part: making the HTTP request, handling the response, and dispatching the right action back into Redux state.
[!TIP] Run this yourself: The full worker saga with axios is in the code-practice repo. Run
node store.jsto see the pending/fulfilled/rejected cycle.
Two Effects: put and yield with a Promise
Two concepts cover everything inside a worker saga.
put is the saga equivalent of dispatch. Instead of calling store.dispatch(action), you yield put(action). The put effect is imported from redux-saga/effects alongside takeEvery.
yield in front of a promise is the saga equivalent of await. Sagas are generators, not async functions, so await is unavailable. yield axios.get(url) hands the promise to the Saga middleware, which resolves it and resumes the generator with the response value.
The mental model:
| In a regular async function | In a worker saga |
|---|---|
await fetch(url) | yield axios.get(url) |
dispatch(action) | yield put(action) |
The Full Worker Saga
In sagas/tasks.js:
import axios from 'axios';
import { put } from 'redux-saga/effects';
import { FETCH_TASKS_PENDING, FETCH_TASKS_FULFILLED, FETCH_TASKS_REJECTED } from '../actionTypes';
export function* fetchTasksWorker() {
yield put({ type: FETCH_TASKS_PENDING });
try {
const response = yield axios.get('https://jsonplaceholder.typicode.com/todos');
yield put({ type: FETCH_TASKS_FULFILLED, payload: response });
} catch (error) {
yield put({ type: FETCH_TASKS_REJECTED, payload: error });
}
}Three dispatches, three stages: loading, success, failure. The reducer handles each one.
Walking Through the Execution
Line by line:
yield put({ type: FETCH_TASKS_PENDING })dispatches immediately. The reducer setsloading: truebefore the request fires.yield axios.get(url)sends the request and parks the generator. The Saga middleware waits for the promise to resolve.- On success:
yield put({ type: FETCH_TASKS_FULFILLED, payload: response })dispatches the result. The reducer stores the data. - On failure:
yield put({ type: FETCH_TASKS_REJECTED, payload: error })dispatches the error. The reducer stores the error message.
ExpandSaga HTTP flow: worker yields put(PENDING), yields axios.get(), on success yields put(FULFILLED), on error yields put(REJECTED)
How the Result Actions Reach the Reducer
FETCH_TASKS_FULFILLED and FETCH_TASKS_REJECTED are dispatched via put, which sends them through the normal Redux action stream. They pass through the root saga's takeEvery watcher, but takeEvery is only watching for FETCH_TASKS -- not for FETCH_TASKS_FULFILLED. So these result actions flow straight through to the reducer without triggering another worker.
The same pending/fulfilled/rejected pattern from Redux Thunk works here. The difference is that in a Thunk, you dispatch directly. In a Saga, you yield put. The reducer does not know or care which middleware produced the action.
The Essentials
yield put(action)isdispatchinside a saga. Importputfromredux-saga/effectsand always precede it withyield.yield promiseisawaitinside a saga. The generator parks while the Saga middleware resolves the promise. The resolved value becomes the return of thatyieldexpression.FETCH_TASKS_FULFILLEDandFETCH_TASKS_REJECTEDreach the reducer normally. They are not intercepted by the root saga because their action types do not match thetakeEverypattern.- The pending/fulfilled/rejected pattern is middleware-agnostic. The reducer handles the same three action types regardless of whether Thunk or Saga produced them.
Further Reading and Watching
- The Best Explanation of How Redux Works (Stephen Grider): covers how reducers process dispatched actions -- the exact mechanism
putfeeds into, and the clearest explanation of why result actions land in the reducer without further interception - Error handling in redux-saga: the official guide to try/catch inside sagas and what happens when effects throw
Practice what you just read.
Keep reading