Je démarre une minuterie pour un chronomètre React composant lorsqu'une action START est envoyée:
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
*/
J'ai du mal à arrêter la saga lorsque l'action STOP
est envoyée depuis mon composant. J'ai essayé d'utiliser les effets cancel
et cancelled
depuis ma saga de travail:
if(yield(take(STOP)) {
yield cancel(timerTickWorkerSaga)
}
ainsi que l'approche dans le premier bloc de code où j'essaie d'arrêter la saga du service de surveillance.
Il semble que quelques choses se passent ici:
cancel
prend un objet Task
comme argument . Ce que vous y passez dans le code ci-dessus est juste le GeneratorFunction
qui crée l'objet saga/Generator. Pour une excellente introduction aux générateurs et à leur fonctionnement, consultez cet article .Vous utilisez yield*
Avant les générateurs takeEvery
et takeLatest
. Utiliser yield*
étalera toute la séquence . Vous pouvez donc y penser comme ceci: que cela remplit la ligne
yield* takeEvery([START, TICK], timerTickWorkerSaga)
avec
while (true) {
const action = yield take([START, TICK])
yield fork(timeTickWorkerSaga, action)
}
Et je ne pense pas que c'est ce que vous cherchez, car je pense que cela finira par bloquer la deuxième ligne de votre timerTickSaga
. Au lieu de cela, vous voulez probablement:
yield fork(takeEvery, [START, TICK], timerTickWorkerSaga)
Cela supprime l'effet takeEvery
pour ne pas bloquer la ligne suivante.
Le deuxième argument que vous passez dans takeLatest
n'est qu'un objet - un objet d'effet CANCEL . Le deuxième argument de takeLatest
doit en fait être un GeneratorFunction
, qui sera exécuté lorsqu'une action correspondant au modèle STOP
est envoyée au magasin Redux. Donc ça devrait vraiment être une fonction de saga. Vous voulez que cela annule la tâche fork(takeEvery, [START, TICK], timerTickWorkerSaga)
afin que les futures actions START
et TICK
n'entraînent pas l'exécution de timerTickWorkerSaga
. Pour ce faire, la saga doit exécuter un effet CANCEL
avec l'objet Task
résultant de l'effet fork(takeEvery...
. Nous pouvons l'objet Task
comme argument supplémentaire à la saga takeLatest
. Nous nous retrouvons donc avec quelque chose comme:
export default function * timerTickSaga () {
const workerTask = yield fork(takeEvery, [START, TICK], timerTickWorkerSaga)
yield fork(takeLatest, STOP, cancelWorkerSaga, workerTask)
}
function* cancelWorkerSaga (task) {
yield cancel(task)
}
Pour des références supplémentaires, consultez le exemple d'annulation de tâche dans les documents redux-saga. Si vous regardez dans la saga main
, vous verrez comment l'effet fork
produit un objet/descripteur Task
qui est utilisé plus bas lors de la production de cancel
effet.
Redux-Saga a une méthode pour cela maintenant, elle s'appelle race race
. Il exécutera 2 tâches, mais lorsque l'une se terminera, elle annulera automatiquement l'autre.
watchStartTickBackgroundSaga est toujours en cours d'exécution
export function* watchStartTickBackgroundSaga() {
yield takeEvery([START, TICK], function* (...args) {
yield race({
task: call(timerTickWorkerSaga, ...args),
cancel: take(STOP)
})
})
}
La réponse de rayd est très correcte mais un peu superflue dans la mesure où takeEvery et takeLatest en interne font un fork. Vous pouvez voir l'explication ici :
Le code devrait donc être:
export default function* timerTickSaga() {
const workerTask = yield takeEvery([START, TICK], timerTickWorkerSaga);
yield takeLatest(STOP, cancelWorkerSaga, workerTask);
}
function* cancelWorkerSaga(task) {
yield cancel(task);
}