I've been fighting with this code for days now and I'm still not getting it right.
The problem: I'm working with a form that has a dropzone. On submit handler, I need to save the images' url in an array, but it's always returning as an empty array.
Declaring images array:
const [images, setImages] = useState([]);
Here I get the images' url and try to save them in the array:
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);
}
})
}
Here I have the submitHandler function where I call the handleSubmit():
const submitHandler = (e) => {
e.preventDefault();
handleSubmit();
dispatch(
createCard(
name,
images,
)
);
}
I know it's because of the order it executes the code but I can't find a solution. Thank you very much in advance!!!!
React state updates are asynchronously processed, but the state updater function itself isn't async
so you can't wait for the update to happen. You can only ever access the state value from the current render cycle. This is why images
is likely still your initial state, an empty array ([]
).
const submitHandler = (e) => {
e.preventDefault();
handleSubmit(); // <-- enqueues state update for next render
dispatch(
createCard(
name,
images, // <-- still state from current render cycle
)
);
}
I think you should rethink how you compute the next state of images
, do a single update, and then use an useEffect
hook to dispatch the action with the updated state value.
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]);