What
Redux-Saga is here for you to help with side effects in your app, especially if those side effects are impacting your global state - Redux store. It is a redux-middleware, which handle async tasks with javascript generators.
Why
You can think of Redux-Saga as of separate thread, where in synchronous way a very complex asynchronous scenarios can be described. Thanks to this it is much easier for developer to read, understand and maintain complex async business logic. In my experience, I have learnt that Redux-Saga is more developer friendly (after you will learn it basis) comparing to Redux Middleware or Redux Thunk
How
In main.js
file plug Root Saga from saga.js
to the store using applyMiddleware
function.
main.js
import { createStore, applyMiddleware } from 'redux'
import createSagaMiddleware from 'redux-saga'
import reducer from './reducers'
import rootSaga from './sagas'
// create the saga middleware
const sagaMiddleware = createSagaMiddleware()
// mount it on the Store
const store = createStore(
reducer,
applyMiddleware(sagaMiddleware)
)
// then run the saga
sagaMiddleware.run(rootSaga)
Your Root Saga just combine all other sagas (generators).
sagas.js
export default function* rootSaga() {
yield all([
firstSaga(),
...
actionWatcher(),
...
lastSaga()
])
}
Examples
Fetch data
export function* fetchData(action) {
try {
// make API call using `Api.fetchUser` function (could be fetch) for
// url stored in variable `action.payload.url`
const data = yield call(Api.fetchUser, action.payload.url)
// send `FETCH_SUCCEEDED` action with payload `data`, it will reach
// all reducers and sagas that are 'listening' to it
yield put({type: "FETCH_SUCCEEDED", data})
} catch (error) {
// send `FETCH_FAILED` action to inform about error with API call
yield put({type: "FETCH_FAILED", error})
}
}
Execute logic after multiple actions
function* businessFlow() {
while (true) {
// wait for the ACTION_1
yield take('ACTION_1')
// ... perform the logic
// wait for the ACTION_2
yield take('ACTION_2')
// ... perform some other logic
}
}
Race
function* someTask() {
while (true) { ... }
}
function* watchTask() {
// `someTask` will be cancelled when CANCEL_TASK will be caught by the
// Saga
yield race({
task: call(someTask),
cancel: take('CANCEL_TASK')
})
}
Cancel previous call
If multiple GET_DATA
actions will be fired, only the last one will be processed (all previous calls of fetchData
will be cancelled)
function* fetchData() {
try {
const data = yield call(Api.fetchUser, action.payload.url)
yield put({type: "FETCH_SUCCEEDED", data})
} catch (error) {
yield put({type: "FETCH_FAILED", error})
}
}
function* actionWatcher() {
yield takeLatest('GET_DATA', fetchData)
}
This was only short highlight of the Redux-Saga, for more check the docs
Top comments (1)
Loved the explanation. Would you please explain when do we use 'put', 'all', 'race' etc?