redux-saga:取消同一动作类型/传奇的多个任务

时间:2017-10-12 23:53:06

标签: reactjs redux redux-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中,他们的示例适用于需要在之后执行取消操作的单个任务。我无法弄清楚如何将其扩展到我的用例。

1 个答案:

答案 0 :(得分:1)

我想到的是以下内容:

componentWillMount中,您通过调度操作并将任务存储在减速器中来注册组件,如下所示:

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