Jack The Baker
Jack The Baker

Reputation: 1903

ReactJS upload multiple image in base64 into an array

I want to create an image uplaoder based on base64 and I want to get results as an array but I got empty! array, I know maybe it's a asynchronous issue, but I don't know how to use async, await in map any idea?

const [array, setArray] = useState([]);

const fileBase64 = (img) => {
    let result = [...img];

    setUrlImage(img);

    result && result.map(function (img){
        let fileReader = new FileReader();
        fileReader.readAsDataURL(img);
        fileReader.onloadend = async () => {
            let res = await fileReader.result;
            setArray([...array, res])
        };
    })
    console.log(array)
}

const handleImage = (e) => {
    let image = [...e.target.files];
    fileBase64(image);
}

<input type="file" multiple={true} onChange={handleImage}/>

Upvotes: 0

Views: 2437

Answers (2)

hgmercury
hgmercury

Reputation: 1

Referencing @pedram 's answer, there are a few things I did to make it work for my case.

  1. instead of .map(this.readAsDataURL) I did .map(fileBase64) as we want to call this function on every single file when trying to upload multiple files collectively.
  2. If you want to push the urls to an existing array, it should be setArray((prevArray)=>[...prevArray,...urls]) where prevArray is the previous state of the array, due to useState being asynchronous.

Upvotes: 0

Pedram
Pedram

Reputation: 16615

Due to this asynchronous nature, state is being set i.e. push before data urls are set in array.

And that's the reason your your array return empty.

To fix it, you can use create Promise which gets resolved after load event of each file. And use Promise.all which would be resolved after each Promise has resolved and then use setArray:

fileBase64  = (img) => {
  return new Promise((resolve, reject) => {
    let fileReader = new FileReader();
    fileReader.onerror = reject
    fileReader.onload = function () {
      resolve(fileReader.result)
    }
    fileReader.readAsDataURL(img)
  })
}

handleImage = (e) => {
  let image = e.target.files;
  Promise.all(Array.from(image).map(this.readAsDataURL))
    .then((urls) => {
      setArray(urls)
    })
    .catch((error) => {
      console.error(error)
    })
}

Upvotes: 3

Related Questions