使用 ejectSaga 取消 saga 任务时,不会触发 yield cancelled()

When cancelling a saga task with ejectSaga, yield cancelled() isn't triggering

我正在使用 react-boilerplate injectSaga/ejectSaga 功能 (reference),但我 运行 在尝试取消我的 saga 时遇到了问题。

tl;dr - 当注入传奇的组件被卸载时,传奇取消通过 task.cancel() 发生,但这不会在实际传奇本身中触发(即, yield cancelled() 未触发)

我像这样将我的 saga 注入到我的容器中(使用 react-router v4 和 redux):

const enhance = compose(
  withRouter, // from react-router 4
  injectSaga({ key: 'stuffDirectory', saga: stuffDirectorySaga }),
  connect(mapStateToProps, mapDispatchToProps),
  // other things that we're composing
);

injectSaga 函数执行以下魔法:

export default ({ key, saga, mode }) => WrappedComponent => {
  class InjectSaga extends React.Component {
    static WrappedComponent = WrappedComponent;
    static contextTypes = {
      store: PropTypes.object.isRequired,
    };
    static displayName = `withSaga(${WrappedComponent.displayName ||
      WrappedComponent.name ||
      'Component'})`;

    componentWillMount() {
      const { injectSaga } = this.injectors;

      injectSaga(key, { saga, mode }, this.props);
    }

    componentWillUnmount() {
      const { ejectSaga } = this.injectors;

      ejectSaga(key);
    }

    injectors = getInjectors(this.context.store);

    render() {
      return <WrappedComponent {...this.props} />;
    }
  }

  return hoistNonReactStatics(InjectSaga, WrappedComponent);
};

这是我的故事(非常简单):

export function* watchSizesDraftsSaga() {
  try {
    let stuffWeLookingFor = yield select(selectStuff());

    // waiting for stuff here

    yield put( moveStuffOver(stuffWeLookingFor) );
    // everything works fine here, page works as expected and stuff has been found
  } finally {
    if (yield cancelled()) {
      // PROBLEM: when the saga is ejected via ejectSaga, this condition never happens
      yield put( cleanStuffUp() );
    }
  }
}

saga 注入工作正常 - 每次安装组件时,我都可以清楚地看到在 redux 中调用和捕获的操作。在 react-boilerplate 的特定 sagaInjectors.js 文件中,我找到了取消注入传奇的特定行:

if (Reflect.has(store.injectedSagas, key)) {
  const descriptor = store.injectedSagas[key];
  if (descriptor.mode && descriptor.mode !== DAEMON) {
    // the task is cancelled here
    descriptor.task.cancel();
    // we verify that the task is cancelled here - descriptor.task.isCancelled() returns true if we log it

    // Clean up in production; in development we need `descriptor.saga` for hot reloading
    if (process.env.NODE_ENV === 'production') {
      // Need some value to be able to detect `ONCE_TILL_UNMOUNT` sagas in `injectSaga`
      store.injectedSagas[key] = 'done'; // eslint-disable-line no-param-reassign
    }
  }
}

尽管任务已成功取消,但我的 yield cancelled() 块从未 运行。关于 saga 取消的工作原理,我是否遗漏了什么?每当 saga 即将被取消时,有没有一种简单的方法可以让我轻松处理我的 saga 清理逻辑?

我认为你误解了 redux-saga 中任务取消的工作原理。

  1. 您对应 watchSizesDraftsSaga() 的任务自行完成。
  2. 然后task.cancel()什么也不做。

yield cancelled() 块只有在您取消任务时才会执行,而任务仍然是 运行。