0

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}/>
5
  • you need to place your setArray logic in the onloadend callback. Commented Feb 5, 2022 at 20:03
  • The results are not available when using console.log(array) because the callback has not yet been invoked. Commented Feb 5, 2022 at 20:03
  • @MaartenDev so what's the solution? can you provide answer? Commented Feb 5, 2022 at 20:23
  • Move the setArray(array) call inside the . onloadend callback Commented Feb 5, 2022 at 20:31
  • @MaartenDev still return empty array, but after select second image it give me an array contains first selected image, not both, any solution? -- code updated Commented Feb 6, 2022 at 7:09

2 Answers 2

3

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)
    })
}
Sign up to request clarification or add additional context in comments.

Comments

0

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.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.