Cancel the saga when the action is sent using the redux saga

I start a timer for the stopwatch. I react the component when the START action is sent:

import 'babel-polyfill' import { call, put } from 'redux-saga/effects' import { delay, takeEvery, takeLatest } from 'redux-saga' import { tick, START, TICK, STOP } from './actions' const ONE_SECOND = 1000 export function * timerTickWorkerSaga (getState) { yield call(delay, ONE_SECOND) yield put(tick()) } export default function * timerTickSaga () { yield* takeEvery([START, TICK], timerTickWorkerSaga) yield* takeLatest(STOP, cancel(timerTickWorkerSaga)) } /* The saga should start when either a START or a TICK is dispatched The saga should stop running when a stop is dispatched */ 

I have a problem stopping the saga when a STOP action STOP sent from my component. I tried using the cancel and cancelled effects from my working saga:

 if(yield(take(STOP)) { yield cancel(timerTickWorkerSaga) } 

as well as the approach in the first block of code where I try to stop the saga from the surveillance service.

+7
javascript redux redux-saga
source share
3 answers

It looks like several things are happening here:

  • The side effect of cancel takes a Task object as an argument . What you pass to it in the above code is just a GeneratorFunction that creates a saga / generator object. For great input on generators and how they work, check out this article .
  • You use yield* before the takeEvery and takeLatest . Using yield* will propagate the entire sequence . So you can think of it this way: that it fills the line

    yield* takeEvery([START, TICK], timerTickWorkerSaga)

    from

     while (true) { const action = yield take([START, TICK]) yield fork(timeTickWorkerSaga, action) } 

    And I don’t think that this is what you are going to do, because I believe that this will block the second line of your timerTickSaga . Instead, you probably want to:

     yield fork(takeEvery, [START, TICK], timerTickWorkerSaga) 

    This rejects the takeEvery effect, so it does not block the next line.

  • The second argument that you pass to takeLatest is just an object - a CANCEL object. The second argument to takeLatest should actually be a GeneratorFunction , which will be executed when an action matching the STOP pattern STOP sent to the Redux repository. So it really should be a function of the saga. You want this to cancel the fork(takeEvery, [START, TICK], timerTickWorkerSaga) so that future START and TICK actions do not timerTickWorkerSaga . You can achieve this if the saga starts the cancel effect with the Task object, which is the result of the fork(takeEvery... effect. We can Task object as an additional argument in the takeLatest saga. So, we get something like:

     export default function * timerTickSaga () { const workerTask = yield fork(takeEvery, [START, TICK], timerTickWorkerSaga) yield fork(takeLatest, STOP, cancelWorkerSaga, workerTask) } function* cancelWorkerSaga (task) { yield cancel(task) } 

For more help, check out the cancellation example in redux-saga docs. If you look in the main saga there, you will see how the fork effect gives the Task object / descriptor, which is used below when it gives the cancel effect.

+9
source share

The answer from rayd is very correct, but a little redundant is that takeEvery and takeLatest internally make the plug. You can see the explanation here :

Thus, the code should be:

 export default function* timerTickSaga() { const workerTask = yield takeEvery([START, TICK], timerTickWorkerSaga); yield takeLatest(STOP, cancelWorkerSaga, workerTask); } function* cancelWorkerSaga(task) { yield cancel(task); } 
+4
source share

Redux-Saga has a method for doing this, it is called race race . He will complete 2 tasks, but when he ends, he will automatically cancel another.

  • https://redux-saga.js.org/docs/advanced/RacingEffects.html

  • watchStartTickBackgroundSaga always works

  • Each time you start or tick off, start the race between the TorkWorkerSaga timer and listen to the next STOP action.
  • When one of these tasks completes, the other task cancels this race behavior.
  • The names "task" and "cancel" inside the race do not matter, they just help the readability of the code.

 export function* watchStartTickBackgroundSaga() { yield takeEvery([START, TICK], function* (...args) { yield race({ task: call(timerTickWorkerSaga, ...args), cancel: take(STOP) }) }) } 
+1
source share

All Articles