Отменить сагу, когда действие отправляется с redux-saga
я запускаю таймер для компонента реакции секундомера, когда отправляется действие запуска:
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
*/
у меня проблемы с остановкой саги, когда STOP
действие отправляется из моего компонента. Я пробовал использовать cancel
и cancelled
эффекты из моей рабочей саги:
if(yield(take(STOP)) {
yield cancel(timerTickWorkerSaga)
}
а также подход, в первом блоке кода, где я пытаюсь остановить саги от просмотра услуги.
3 ответов
похоже, что здесь происходит несколько вещей:
- на
cancel
побочный эффект принимаетTask
объект в качестве аргумента. То, что вы передаете в него в коде выше, - это простоGeneratorFunction
это создает объект saga / Generator. Для большого вступления в генераторы и как они работают, проверьте в этой статье. -
вы используете
yield*
доtakeEvery
иtakeLatest
генераторы. Используяyield*
будет распространение вся последовательность. Поэтому вы можете думать об этом так: что он заполняет строкуyield* takeEvery([START, TICK], timerTickWorkerSaga)
С
while (true) { const action = yield take([START, TICK]) yield fork(timeTickWorkerSaga, action) }
и я не думаю, что это то, что вы собираетесь, потому что я считаю, что это в конечном итоге блокирует вторую строку вашего
timerTickSaga
. Вместо этого вы, вероятно, хотите:yield fork(takeEvery, [START, TICK], timerTickWorkerSaga)
это вилка от
takeEvery
эффект, поэтому он не блокирует следующую строку. -
второй аргумент, который вы переход в
takeLatest
- это просто объект -отменить объект эффекта. Второй аргумент кtakeLatest
должно бытьGeneratorFunction
, который будет запущен, когда действие, соответствующееSTOP
шаблон отправляется в магазин вместе. Так что это действительно должна быть функция saga. Вы хотите, чтобы это отменитьfork(takeEvery, [START, TICK], timerTickWorkerSaga)
задач, так что будущееSTART
иTICK
действия не вызываютtimerTickWorkerSaga
запустить. Вы можете достичь этого, имея сагу запуститьCANCEL
СTask
объект, возникший в результатеfork(takeEvery...
эффект. Мы можемTask
объект как дополнительный аргумент доtakeLatest
сага. Таким образом, мы получаем что-то вроде:export default function * timerTickSaga () { const workerTask = yield fork(takeEvery, [START, TICK], timerTickWorkerSaga) yield fork(takeLatest, STOP, cancelWorkerSaga, workerTask) } function* cancelWorkerSaga (task) { yield cancel(task) }
для дополнительной справки проверьте пример отмены задачи в документах redux-saga. Если вы посмотрите в main
сага там, вы увидите, как fork
эффект дает Task
объект / дескриптор, который используется ниже при подаче the cancel
эффект.
ответ от rayd очень правильный, но немного лишний в том, как takeEvery и takeLatest внутренне делают вилку. Вы можете увидеть объяснение здесь:
поэтому код должен быть:
export default function* timerTickSaga() {
const workerTask = yield takeEvery([START, TICK], timerTickWorkerSaga);
yield takeLatest(STOP, cancelWorkerSaga, workerTask);
}
function* cancelWorkerSaga(task) {
yield cancel(task);
}
Redux-Saga имеет метод для этого сейчас, это называется race race
. Он будет запускать 2 задачи, но когда один закончит, он автоматически отменит другой.
watchStartTickBackgroundSaga всегда работает
- каждый раз, когда есть старт или ТИК, начать гонку между timerTickWorkerSaga и слушать для следующей остановки действие.
- когда одна из этих задач завершается, другая задача отменяется это поведение расы.
- названия "задача" и "отмена" внутри гонки не имеют значения, они просто помогают читаемости кода
export function* watchStartTickBackgroundSaga() {
yield takeEvery([START, TICK], function* (...args) {
yield race({
task: call(timerTickWorkerSaga, ...args),
cancel: take(STOP)
})
})
}