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
    }
}