如何让 React Redux 异步操作返回承诺?

时间:2021-03-23 10:50:22

标签: javascript reactjs redux es6-promise redux-thunk

我有一个返回承诺的 redux 操作函数。在该函数体内,还有另一个异步函数,它在完成调用时返回一个回调。然后我在另一个地方调用这个函数,用 .then() 链接它,但是在浏览器中用断点调试它时,promise 函数存在于前两行代码之后

return new Promise((resolve, reject) => {
    return (dispatch, getState) => {

是不是因为第二个return语句?这是 react/redux 代码,因此我的异步 Thunk redux 操作必须有第二个 return 语句。我怎样才能做到这一点?这样我就可以在另一个 redux 操作中调用它并将它与 handleProfileImageUploadToS3().then(() => {...}) 链接起来?

完整的函数体:

export const handleProfileImageUploadToS3 = () => {
  return new Promise((resolve, reject) => {
    return (dispatch, getState) => {
      const settingsState = getState().BusinessSettings
      const userState = getState().User
      const imageUpload = true

      if (!settingsState.get('logoImage') || settingsState.get('logoImage') === null) {
        reject('no image selected')
        return
      }
      Utilities.uploadFileToS3(
        imageUpload,
        settingsState.get('logoImage'),
        'apiurl',
        `profiles/${userState.get('id')}`,
        (error, url) => {
          if (error) {
            dispatch(uploadProfileSettingsImageError(error))
            return
          }
          dispatch(updateProfileImageUrlAfterUpload(url))
          resolve(url)
        }
      )
    }
  })
}

2 个答案:

答案 0 :(得分:1)

我没有看到你的整个代码库,但我担心这个函数中有一些危险信号。

  • 您状态的 BusinessSettingsUser 属性似乎是带有 get() 方法的 non-serializable 对象。
  • 如果 S3 上传出错,那么您 dispatchreturn 但您永远不会resolvereject Promise
  • 来自reject('no image selected')的拒绝不太可能在任何地方被发现。
  • 似乎未上传的图像应该存储在您的 UI 状态中并作为参数传递,而不是存储在 Redux 中。您可能希望在上传后存储 URL。这也消除了拒绝的需要。

您正在解决此功能中的两个独立问题,我建议您将两者分开。

首先,您有一个使用成功/失败回调的函数 Utilities.uploadFileToS3,并且您想将其转换为异步函数(返回 Promise 的函数)。

我会做一个助手,它只接受变化的参数并忽略那些不变的参数(如 'apiurl')。

const asyncUploadFileToS3 = (image, userId) => {
  return new Promise((resolve, reject) => {
    Utilities.uploadFileToS3(
      true,
      image,
      "apiurl",
      `profiles/${userId}`,
      (error, url) => (url ? resolve(url) : reject(error))
    );
  });
};

既然你已经解决了这部分,你可以用更典型的方式来编写 thunk。您可以通过 Promise 链接或通过创建函数 .then() 并使用 async/try 块来返回 catch。由于我们已经在 new Promise 函数中处理了这个问题,因此我们不需要将整个内容包装在 asyncUploadFileToS3 中。

可以从 thunk 返回一个结果并将其链接起来,但我不确定什么在这里最有意义。

export const handleProfileImageUploadToS3 = (image) => 
  async ( dispatch, getState ) => {
    const userId = getState().user.id;
    try {
      const url = await asyncUploadFileToS3(image, userId);
      dispatch(updateProfileImageUrlAfterUpload(url));
      return "this is the result";
    } catch (error) {
      dispatch(uploadProfileSettingsImageError(error));
      return "there was an error";
    }
  };
export default function App() {
  const dispatch = useDispatch();

  const avatar = useSelector((state) => state.user.imageUrl);

  const onClick = () => {
    const image = new Blob();
    dispatch(handleProfileImageUploadToS3(image)).then(
      // logs "this is the result" or "there was an error"
      (returned) => console.log("finished", returned)
    );
  };

  return (
    <div>
      <button onClick={onClick}>Upload</button>
      {avatar ? <div>Avatar URL: {avatar}</div> : <div>No Avatar</div>}
    </div>
  );
}

Code Sandbox Demo

答案 1 :(得分:0)

经过更多研究,我发现您必须在返回 new Promise 之前从初始 Promise 函数返回另一个函数才能访问 Redux dispatchgetState 方法,而不是将其作为匿名函数就在 new Promise() 代码块中,然后退出函数执行。完整代码如下:

export const handleProfileImageUploadToS3 = () => (dispatch, getState) => {
  return new Promise((resolve, reject) => {
    const settingsState = getState().BusinessSettings
    const userState = getState().User
    const imageUpload = true

    if (!settingsState.get('logoImage') || settingsState.get('logoImage') === null) {
      reject('no image selected')
      return
    }
    Utilities.uploadFileToS3(
      imageUpload,
      settingsState.get('logoImage'),
      'apiurl',
      `profiles/${userState.get('id')}`,
      (error, url) => {
        if (error) {
          dispatch(uploadProfileSettingsImageError(error))
          return
        }
        dispatch(updateProfileImageUrlAfterUpload(url))
        resolve(url)
      }
    )
  })
}