redux-saga:取消相同动作类型/传奇的多个任务
redux-saga: Cancelling multiple tasks of the same action type / saga
我正在尝试弄清楚如何在 redux-saga 中合并取消多个相同操作类型的任务。基本上,当我的组件处于 componentWillUnmount()
时,我想取消它可能已经启动的所有任务。
如果我有以下操作(这大大简化了我在代码中的实际操作,但我试图将其精简到最基本的部分):
// Action
export const loadMyData = params => {
let url = /* Create URL based on params */
return {
type: FETCH_DATA,
url,
key: /* a unique key so we can retrieve it in the state later */
}
}
以及以下传奇:
// Saga
export function* fetchData(action) {
try {
// 'callApi()' currently uses axios
let response = yield call(callApi, action.url);
yield put({
type: FETCH_SUCCESS,
key: action.key,
payload: response.data
});
} catch(error) {
yield put({
type: FETCH_FAILURE,
error
});
}
}
export function* watchFetchData() {
while (true) {
let action = yield take(FETCH_DATA);
let task = yield fork(fetchApi, action);
}
}
如上所述,组件可能会多次调用 loadMyData()
。此外,可能还有其他组件也调用 loadMyData()
。因此,我试图找到一种方法来仅从处于 componentWillUnmount()
状态的组件取消任务,但保留任何其他 运行 任务不变。
在 Redux Saga Cancellation documentation 中,他们的示例针对的是单个任务,该任务之后需要取消操作。而且我不知道如何将其扩展到我的用例。
我想到的是:
在 componentWillMount
中,您通过分派操作并将任务存储在 reducer 中来注册组件,如下所示:
registerWatchFetchData = (componentKey) => {
return {
type: "REGISTER_WATCH_FETCH_DATA",
payload: { componentKey }
}
}
减速器:
// ...
case "REGISTER_WATCH_FETCH_DATA":
return {...state, tasks: {...state.tasks, [action.payload.componentKey]: []}}
然后在 function* watchFetchData()
中,将新任务存储在 reducer 中,用于在 payload
:
中提供的 componentKey 上的相应组件键控
export function* watchFetchData() {
while (true) {
let action = yield take(FETCH_DATA);
let task = yield fork(fetchApi, action);
yield put({ type: "STORE_TASK", payload: { componentKey: action.payload.componentKey, task } })
}
}
然后加入reducer
// ...
case "STORE_TASK":
return {...state, tasks: {...state.tasks, [action.payload.componentKey]: [...state.tasks[action.payload.componentKey], action.payload.task]}}
并且在 componentWillUnmount
中,您分派另一个操作来告诉 saga 为 componentKey 提取所有任务,遍历它们并取消它们,如下所示:
function* watchUnregisterComponent(){
while(true){
const action = yield take("UNREGISTER_WATCH_FETCH_DATA")
const componentTasks = yield select(state => state.myReducer.tasks[action.payload.componentKey])
componentTasks.forEach((t) => {
yield cancel(t)
})
// dispatch another action to delete them from the reducer
}
}
我正在尝试弄清楚如何在 redux-saga 中合并取消多个相同操作类型的任务。基本上,当我的组件处于 componentWillUnmount()
时,我想取消它可能已经启动的所有任务。
如果我有以下操作(这大大简化了我在代码中的实际操作,但我试图将其精简到最基本的部分):
// Action
export const loadMyData = params => {
let url = /* Create URL based on params */
return {
type: FETCH_DATA,
url,
key: /* a unique key so we can retrieve it in the state later */
}
}
以及以下传奇:
// Saga
export function* fetchData(action) {
try {
// 'callApi()' currently uses axios
let response = yield call(callApi, action.url);
yield put({
type: FETCH_SUCCESS,
key: action.key,
payload: response.data
});
} catch(error) {
yield put({
type: FETCH_FAILURE,
error
});
}
}
export function* watchFetchData() {
while (true) {
let action = yield take(FETCH_DATA);
let task = yield fork(fetchApi, action);
}
}
如上所述,组件可能会多次调用 loadMyData()
。此外,可能还有其他组件也调用 loadMyData()
。因此,我试图找到一种方法来仅从处于 componentWillUnmount()
状态的组件取消任务,但保留任何其他 运行 任务不变。
在 Redux Saga Cancellation documentation 中,他们的示例针对的是单个任务,该任务之后需要取消操作。而且我不知道如何将其扩展到我的用例。
我想到的是:
在 componentWillMount
中,您通过分派操作并将任务存储在 reducer 中来注册组件,如下所示:
registerWatchFetchData = (componentKey) => {
return {
type: "REGISTER_WATCH_FETCH_DATA",
payload: { componentKey }
}
}
减速器:
// ...
case "REGISTER_WATCH_FETCH_DATA":
return {...state, tasks: {...state.tasks, [action.payload.componentKey]: []}}
然后在 function* watchFetchData()
中,将新任务存储在 reducer 中,用于在 payload
:
export function* watchFetchData() {
while (true) {
let action = yield take(FETCH_DATA);
let task = yield fork(fetchApi, action);
yield put({ type: "STORE_TASK", payload: { componentKey: action.payload.componentKey, task } })
}
}
然后加入reducer
// ...
case "STORE_TASK":
return {...state, tasks: {...state.tasks, [action.payload.componentKey]: [...state.tasks[action.payload.componentKey], action.payload.task]}}
并且在 componentWillUnmount
中,您分派另一个操作来告诉 saga 为 componentKey 提取所有任务,遍历它们并取消它们,如下所示:
function* watchUnregisterComponent(){
while(true){
const action = yield take("UNREGISTER_WATCH_FETCH_DATA")
const componentTasks = yield select(state => state.myReducer.tasks[action.payload.componentKey])
componentTasks.forEach((t) => {
yield cancel(t)
})
// dispatch another action to delete them from the reducer
}
}