Hi I am trying to save multiple image files base64 into state in array.
const uploadImage = e => {
if (e.target.files && e.target.files.length > 0) {
// ...
let imagesArray = [];
for (let i = 0; i < e.target.files.length; i++) {
const image = e.target.files[i];
const verified = verifyImage(image);
if (verified) {
const reader = new FileReader();
const type = image.type;
reader.addEventListener('load', () => {
let imgObj = {
base64: reader.result,
name: image.name,
type,
size: image.size,
};
imagesArray.push(imgObj)
});
reader.readAsDataURL(image);
}
}
setImages(imagesArray);
}
e.target.value = null;
}
after updating state its not rendering in map function
{
images.length > 0
? images.map((imageObj, i) => {
return (
<div key={i}>
<img
src={imageObj.base64}
alt=''
/>
<div>
<span>{imageObj.size ? imageObj.size : '-'}</span>
<span>{imageObj.name ? imageObj.name : '-'}</span>
<span
onClick={() => removeImage(i)}
>
{translations(locale).remove}
</span>
</div>
</div>
)
})
: null
}
State array is updated but array.length is still showing as 0. What am i doing wrong ?
Do you have errors in the devtool console ? Got it working here https://stackblitz.com/edit/react-wdku9v
EDIT:
The issue here is that every time the uploadImage method is called it'll push an empty array.
Why ?: when using FileReader you are attaching an event to it waiting it to load in an "async"? way. But your for loop do not wait so here is a description of what happens in the uploadImage.
Given 1 image by the input :
imagesArray is initialized as an empty array : []
for loop start :
"load" event is not called yet
for loop stop
we change state with setImages(imagesArray) BUT, at this time imagesArray is still empty leading to a never showing image
"load" event is called or later or before, we don't know.
To prevent this from happening we can create another method that encapsulate the file reader in an Promise like so :
const fileToDataUri = (image) => {
return new Promise((res) => {
const reader = new FileReader();
const {type, name, size} = image;
reader.addEventListener('load', () => {
res({
base64: reader.result,
name: name,
type,
size: size,
})
});
reader.readAsDataURL(image);
})
}
and in the for loop we will concat all promises and await them in a Promise.all
const newImagesPromises = []
for (let i = 0; i < e.target.files.length; i++) {
newImagesPromises.push(fileToDataUri(e.target.files[i]))
}
const newImages = await Promise.all(newImagesPromises)