如何将 api 调用包装器从 redux thunk 迁移到 redux saga

How to migrate api call wrapper from a redux thunk to redux saga

我最近开始使用 redux-saga 并且非常喜欢它。

我有以下包装器用于我的 api 调用,它会接受承诺(我的 api 调用),并显示预加载器和处理错误。

export const callApi = (promise: Promise<any>, errorMsg: string = 'Api error') => (dispatch: Dispatch) => {
  dispatch(setLoading(true));
  return promise.then(
    (response) => {
      dispatch(setLoading(false));
      return response.body;
    },
    (error) => {
      dispatch(setLoading(false));
      dispatch(apiError(errorMsg, error));
      return error;
    });
};

我不确定如何在 redux saga 中复制这样的行为。我找不到任何这样的例子吗?


到目前为止我想出了

const camelizeKeysPromise = (obj) => Promise.resolve(camelizeKeys(obj));


export function* sagaCallApi(promise: Promise<any>, errorMsg: string = 'Api error') {
   yield put(setLoading(true));
   try {
      const response = yield call(promise);
      try {
        const result = yield call(camelizeKeysPromise(response.body));
        return result;
      } catch (e) {
        return response.body;
      }
   } catch (exception) {
      yield put(setLoading(false));
      yield put(apiError(errorMsg, error));
    };
}

做出 call 承诺不会 return 想要的回应。您可以使用 redux-saga 中的 eventChannel 创建一个频道,该频道在成功时发出响应或在失败时发出错误对象,然后订阅您的传奇中的频道。

const promiseEmitter = promise => {
  return eventChannel(emit => {
    promise.then(
      response => emit({response}),
      error => emit({error})
    );
  });
};

修改您的新 saga,将对 promise 的调用替换为:

const channel = yield call(promiseEmitter, promise);
const {response, error} = yield take(channel);
if(response){
  // handle success
  return response;
}else if(error){
  // handle failure
  yield put(setLoading(false));
  yield put(apiError(errorMsg, error));
}

请注意,我在没有使用编辑器的情况下编写此代码时可能存在语法错误,但您可以获得一般方法。