如何使 React Redux 异步操作 return 成为一个承诺?
How to make React Redux async action return a promise?
我有一个 return 承诺的 redux 动作函数。在该函数体内,还有另一个异步函数 return 在完成调用时进行回调。然后我在另一个地方调用这个函数,用 .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)
}
)
}
})
}
经过更多研究,我发现您必须 return 在 returning new Promise
之前 return 初始 Promise 函数中的另一个函数才能访问 Redux dispatch
和 getState
方法,而不是 return 在 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)
}
)
})
}
我没有看到你的整个代码库,但我担心这个函数中有一些危险信号。
- 您所在州的
BusinessSettings
和 User
属性似乎是具有 get()
方法的 non-serializable 对象。
- 如果 S3 上传出错,那么您
dispatch
和 return
但您永远不会 resolve
或 reject
Promise
.
reject('no image selected')
的拒绝不太可能在任何地方被发现。
- 似乎未上传的图像应该存储在您的 UI 状态中并作为参数传递,而不是存储在 Redux 中。您可能希望在上传后存储 URL。这也消除了拒绝的需要。
您在此函数中解决了两个不同的问题,我建议您将两者分开。
首先是您有一个使用 success/failure 回调的函数 Utilities.uploadFileToS3
,并且您想将其转换为异步函数(return 是 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。您可以通过 .then()
链接或通过创建函数 async
并使用 try
/catch
块来 return a Promise
。我们不需要将整个东西包装在 new Promise
中,因为我们已经在 asyncUploadFileToS3
函数中处理了它。
你 可以 return 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>
);
}
我有一个 return 承诺的 redux 动作函数。在该函数体内,还有另一个异步函数 return 在完成调用时进行回调。然后我在另一个地方调用这个函数,用 .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)
}
)
}
})
}
经过更多研究,我发现您必须 return 在 returning new Promise
之前 return 初始 Promise 函数中的另一个函数才能访问 Redux dispatch
和 getState
方法,而不是 return 在 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)
}
)
})
}
我没有看到你的整个代码库,但我担心这个函数中有一些危险信号。
- 您所在州的
BusinessSettings
和User
属性似乎是具有get()
方法的 non-serializable 对象。 - 如果 S3 上传出错,那么您
dispatch
和return
但您永远不会resolve
或reject
Promise
. reject('no image selected')
的拒绝不太可能在任何地方被发现。- 似乎未上传的图像应该存储在您的 UI 状态中并作为参数传递,而不是存储在 Redux 中。您可能希望在上传后存储 URL。这也消除了拒绝的需要。
您在此函数中解决了两个不同的问题,我建议您将两者分开。
首先是您有一个使用 success/failure 回调的函数 Utilities.uploadFileToS3
,并且您想将其转换为异步函数(return 是 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。您可以通过 .then()
链接或通过创建函数 async
并使用 try
/catch
块来 return a Promise
。我们不需要将整个东西包装在 new Promise
中,因为我们已经在 asyncUploadFileToS3
函数中处理了它。
你 可以 return 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>
);
}