Root Saga

The root saga runs at startup via saga.run() and routes dispatched actions to worker sagas. It must be a generator function called after createStore().

June 28, 20263 min read2 / 2

The store is wired and the middleware is mounted. The middleware is watching the action stream but has nothing to run. The root saga is the entry point that changes that.

[!TIP] Run this yourself: Root saga setup is in the code-practice repo. Run node store.js to see the root saga log at startup.

What Is the Root Saga?

The root saga is a generator function that executes as soon as saga.run() is called. Its long-term job is organizational: watch for specific action types and route them to worker sagas that handle the actual async work.

Think of it as the dispatcher for the entire saga layer. When the app starts, the root saga starts. When a matching action arrives, the root saga knows which worker saga to hand it off to. Worker sagas handle HTTP requests, the root saga stays alive and keeps watching.

For now it is just a shell. The watchers come in the next post.

Creating It

Create a sagas/ folder inside src/ and add index.js:

JavaScript
// src/sagas/index.js export function* rootSaga() { console.log('root saga invoked'); }

The function* syntax is required. Writing function rootSaga() instead produces a runtime error because Redux Saga calls .next() on whatever saga.run() receives. A regular function does not return an iterator -- generator functions do. An empty generator also triggers a warning: "generator does not yield" -- the next post adds the yield statements that resolve this.

Running It at Startup

In store/index.js, import the root saga and call saga.run() after the store is created:

JavaScript
import { createStore, applyMiddleware } from 'redux'; import { composeWithDevTools } from 'redux-devtools-extension'; import createSagaMiddleware from 'redux-saga'; import allReducers from '../reducers'; import { rootSaga } from '../sagas'; const saga = createSagaMiddleware(); const store = createStore( allReducers, composeWithDevTools(applyMiddleware(saga)) ); saga.run(rootSaga); // must come after createStore export default store;

saga.run() must come after createStore(), never before. Calling it first throws: Before running a Saga, you must mount the Saga middleware on the Store using applyMiddleware. The middleware needs the store to exist before it can receive actions.

Root saga startup sequence: store created, middleware mounted, saga.run called, root saga executes ExpandRoot saga startup sequence: store created, middleware mounted, saga.run called, root saga executes

Why an Empty Generator Warns

An empty root saga starts, logs, and exits. Once it exits, it can never respond to actions again. Redux Saga catches this and warns because a root saga that finishes immediately is almost certainly a bug. The fix is a yield statement that keeps execution alive -- yield takeEvery(...) for example. That is exactly what the next post introduces.

The Essentials

  1. The root saga is the entry point for the saga layer. It runs automatically via saga.run(rootSaga) at startup and routes incoming actions to worker sagas.
  2. It must be a function*. A regular function returns undefined, not an iterator. saga.run() requires an iterator -- calling it with a regular function throws at runtime.
  3. saga.run() must come after createStore(). The middleware must already be mounted. Reversing the order throws immediately.
  4. An empty root saga exits and warns. Without a yield, the saga completes synchronously and stops watching. The next post adds the watcher that keeps it alive.

Further Reading and Watching

Practice what you just read.

Root Saga -- Quiz
1 exercise