0

I have a function that uploads multiple files one at a time and for that, I have to wait till all of them are uploaded and then I want to call a function.

I've two hooks

    const [attachments, setAttachments] = useState([]);
    const [uploadedAttachments, setuploadedAttachments] = useState([]);

attachments contains the selected file and then when a file is uploaded I'm storing some metadata about it in uploadedAttachments which I need to send with another call.

So on the upload button press, I'm calling this function:

async function uploadAttachments(userID, id) {
        await Promise.all(
            attachments.map(async (val, index) => {
                await services.uploadAttachment(val, userID, id).then((res) => {
                    setuploadedAttachments([...uploadedAttachments, { "name": res.name, "size": res.size }])
                }).catch((error) => {
                    Alert.alert("Error", error)
                })
            })).then(() => {
                console.log(JSON.stringify(uploadedAttachments))
            }).catch((e) => {
                console.log("ERROR: " + e)
            })
}

The JSON.stringify(uploadedAttachments) prints [] but if I console.log the res before setuploadedAttachments is called I see I'm getting the response and that means that the setuploadedAttachments is being updated but the place where I'm print it shows me empty array. So, how do I make sure that it await and so that I can see it when the Promise.all is finished?

UPDATE

My question is not a duplicate of useState set method not reflecting change immediately

I'm waiting for all the promises to fulfill. Each promise when return me some data I add it to an array which I'm handling using the useState hook. Yes, it's not reflecting the changes immediately and I have tried putting it in the useEffect hook as suggested in the supposed duplicate question.

useEffect(() => {
     setuploadedAttachments([...uploadedAttachments, { "name": res.name, "size": res.size }])
}, [])

It shows the alert with Error in the title but no error message. In short, it's not working.

UPDATE 2

Based on the comments I did

async function uploadAttachments(userID, id) {
        await Promise.all(
            attachments.map(async (val, index) => {
                await services.uploadAttachment(val, userID, id).then((res) => {
                    return res
                }).catch((error) => {
                    Alert.alert("Error", error)
                })
            })).then((responses) => {
                console.log(JSON.stringify(responses))
            }).catch((e) => {
                console.log("ERROR: " + e)
            })
}

The services.uploadAttachment function is

async function uploadAttachment(val, userID, id) {

    let fullURL = BASE_URL + '/uploadFile'
    
    const data = new FormData()
    data.append(...

    const config = {
       ...
    };

    return new Promise((res, rej) => {
        axios.post(fullURL, data, config).then((response) => {
            res(response.data)
        }).catch((error) => {
            console.log(error)
            Alert.alert("Error Occured", error);
        })
    })
}
Chaudhry Talha
  • 7,231
  • 11
  • 67
  • 116
  • It *does* wait, but `setuploadedAttachments` does not update the `const uploadedAttachments` variable that you have in scope – Bergi Jun 17 '21 at 13:50
  • @Bergi yes, so how do I update it? The question that was tagged as a duplicate does not answer my question as it uses the `useEffect` and I have to wait till all promises are fulfilled and then using the updated state to do additional functionality. – Chaudhry Talha Jun 17 '21 at 14:07
  • Just don't use the updated state. Return the upload responses from your `then` callback, and access the result of the `Promise.all` promise – Bergi Jun 17 '21 at 15:13
  • Also the `useEffect` that was suggested as a solution in the duplicate was supposed to contain the "additional functionality" you're talking about, not the `setuploadedAttachments` call. It's an effect that should run whenever the `uploadedAttachments` (and possibly the `attachments`) state change, taking them as dependencies. – Bergi Jun 17 '21 at 15:19
  • I chose the duplicate mostly because it explains why `console.log(JSON.stringify(uploadedAttachments))` cannot print the results you expected. – Bergi Jun 17 '21 at 15:20
  • @Bergi I'm a bit new to javascript/react rules, so when you say return responses from `then` callback, return it to where? Because the `res` I'm getting with `Promise.all` is returning one-by-one and not a combined result so I need to store the result of one promise when I get it somewhere right? Is it possible to add an answer regarding what you're suggesting? If not then can you please reopen the question so that others can find it to answer? Because I'm not sure what new to add if I add a new question. – Chaudhry Talha Jun 17 '21 at 15:30
  • @Bergi All I want to do is simply wait till all files are uploaded and then use the response of each of those files as soon as the last file is finished uploading. – Chaudhry Talha Jun 17 '21 at 15:32
  • I mean `Promise.all(attachments.map(async (val) => { … return response; })).then(responses => { console.log(responses); })`. Also try to [avoid mixing `.then()` and `await` syntax](https://stackoverflow.com/a/54387912/1048572). – Bergi Jun 17 '21 at 15:45
  • @Bergi I did exactly like you just described. `.then((res) => { return res ... })).then((responses) => { console.log(JSON.stringify(responses)) ...` and I get `[null,null]` printed in the log. – Chaudhry Talha Jun 17 '21 at 16:02
  • Did you unnest the promise chain and actually `return` the value from the `async` function that is the `map` callback? It seems you're still returning `undefined`. – Bergi Jun 17 '21 at 16:11
  • @Bergi just update my question, kindly see UPDATE 2 where it shows exactly how I did it. – Chaudhry Talha Jun 17 '21 at 16:18
  • 1
    Ah, yes. The callback should be either `async (val) => { try { return await services.uploadAttachment(val, userID, id); } catch (error) { Alert.alert("Error", error); } }` or `(val) => { return services.uploadAttachment(val, userID, id).catch((error) => { Alert.alert("Error", error); }); }` - notice the `return`s in both cases. – Bergi Jun 17 '21 at 17:51
  • @Bergi thank you so much for your help & patience :D This last comment solved it for me. Feel free to add it as an answer I'll accept it. – Chaudhry Talha Jun 17 '21 at 18:04

0 Answers0