在 React 中等待 useState
await useState in React
我已经和这个代码斗争了好几天了,但我还是没弄好。
问题:
我正在使用具有拖放区的表单。在提交处理程序中,我需要将图像的 url 保存在一个数组中,但它总是作为一个空数组返回。
正在声明图像数组:
const [images, setImages] = useState([]);
在这里,我获取了图像的 url 并尝试将它们保存在数组中:
const handleSubmit = () => {
files.forEach(async(file)=> {
const bodyFormData = new FormData();
bodyFormData.append('image', file);
setLoadingUpload(true);
try {
const { data } = await Axios.post('/api/uploads', bodyFormData, {
headers: {
'Content-Type': 'multipart/form-data',
Authorization: `Bearer ${userInfo.token}`,
},
});
setImages([...images,data])
setLoadingUpload(false);
} catch (error) {
setErrorUpload(error.message);
setLoadingUpload(false);
}
})
}
这里有 submitHandler 函数,我在其中调用 handleSubmit():
const submitHandler = (e) => {
e.preventDefault();
handleSubmit();
dispatch(
createCard(
name,
images,
)
);
}
我知道这是因为它执行代码的顺序,但我找不到解决方案。
非常感谢您!!!!
为了防止竞争条件,您可以尝试使用 setImages
和当前值,如下所示:
setImages(currentImages => [...currentImages, data])
这样,您将准确使用您所在州当前包含的内容,因为在这种情况下 images
可能不正确。
作为另一个提示,我建议您 map
文件,而不是循环遍历您的文件,如 files.map(...
。有了这个,您可以将所有文件条目映射到一个承诺,最后,将它们合并到一个包含所有请求的承诺。所以你可以简单地看一下。
问题
React 状态更新是异步处理的,但状态更新函数本身不是 async
所以你不能等待更新发生。您只能从当前渲染周期访问状态值。这就是为什么 images
可能仍然是您的初始状态,一个空数组 ([]
)。
const submitHandler = (e) => {
e.preventDefault();
handleSubmit(); // <-- enqueues state update for next render
dispatch(
createCard(
name,
images, // <-- still state from current render cycle
)
);
}
解决方案
我认为您应该重新考虑如何计算 images
的下一个状态,进行一次更新,然后使用 useEffect
挂钩使用更新后的状态值调度操作。
const handleSubmit = async () => {
setLoadingUpload(true);
try {
const imagesData = await Promise.all(files.map(file => {
const bodyFormData = new FormData();
bodyFormData.append('image', file);
return Axios.post('/api/uploads', bodyFormData, {
headers: {
'Content-Type': 'multipart/form-data',
Authorization: `Bearer ${userInfo.token}`,
},
});
}));
setImages(images => [...images, ...imagesData]);
} catch(error) {
setErrorUpload(error.message);
} finally {
setLoadingUpload(false);
}
}
const submitHandler = (e) => {
e.preventDefault();
handleSubmit();
}
React.useEffect(() => {
images.length && name && dispatch(createCard(name, images));
}, [images, name]);
我已经和这个代码斗争了好几天了,但我还是没弄好。
问题: 我正在使用具有拖放区的表单。在提交处理程序中,我需要将图像的 url 保存在一个数组中,但它总是作为一个空数组返回。
正在声明图像数组:
const [images, setImages] = useState([]);
在这里,我获取了图像的 url 并尝试将它们保存在数组中:
const handleSubmit = () => {
files.forEach(async(file)=> {
const bodyFormData = new FormData();
bodyFormData.append('image', file);
setLoadingUpload(true);
try {
const { data } = await Axios.post('/api/uploads', bodyFormData, {
headers: {
'Content-Type': 'multipart/form-data',
Authorization: `Bearer ${userInfo.token}`,
},
});
setImages([...images,data])
setLoadingUpload(false);
} catch (error) {
setErrorUpload(error.message);
setLoadingUpload(false);
}
})
}
这里有 submitHandler 函数,我在其中调用 handleSubmit():
const submitHandler = (e) => {
e.preventDefault();
handleSubmit();
dispatch(
createCard(
name,
images,
)
);
}
我知道这是因为它执行代码的顺序,但我找不到解决方案。 非常感谢您!!!!
为了防止竞争条件,您可以尝试使用 setImages
和当前值,如下所示:
setImages(currentImages => [...currentImages, data])
这样,您将准确使用您所在州当前包含的内容,因为在这种情况下 images
可能不正确。
作为另一个提示,我建议您 map
文件,而不是循环遍历您的文件,如 files.map(...
。有了这个,您可以将所有文件条目映射到一个承诺,最后,将它们合并到一个包含所有请求的承诺。所以你可以简单地看一下。
问题
React 状态更新是异步处理的,但状态更新函数本身不是 async
所以你不能等待更新发生。您只能从当前渲染周期访问状态值。这就是为什么 images
可能仍然是您的初始状态,一个空数组 ([]
)。
const submitHandler = (e) => {
e.preventDefault();
handleSubmit(); // <-- enqueues state update for next render
dispatch(
createCard(
name,
images, // <-- still state from current render cycle
)
);
}
解决方案
我认为您应该重新考虑如何计算 images
的下一个状态,进行一次更新,然后使用 useEffect
挂钩使用更新后的状态值调度操作。
const handleSubmit = async () => {
setLoadingUpload(true);
try {
const imagesData = await Promise.all(files.map(file => {
const bodyFormData = new FormData();
bodyFormData.append('image', file);
return Axios.post('/api/uploads', bodyFormData, {
headers: {
'Content-Type': 'multipart/form-data',
Authorization: `Bearer ${userInfo.token}`,
},
});
}));
setImages(images => [...images, ...imagesData]);
} catch(error) {
setErrorUpload(error.message);
} finally {
setLoadingUpload(false);
}
}
const submitHandler = (e) => {
e.preventDefault();
handleSubmit();
}
React.useEffect(() => {
images.length && name && dispatch(createCard(name, images));
}, [images, name]);