I am working with react functional component.
I have download(async) functionality in my application. There is list of download buttons in my app. I want to download them one by one in queue.
const [downloadQueue, setDownloadQueue] = useState([]);
onClickDonwload = () => { //on every download click this function calls. So user can click on download continuously but I want to make sure them to be downloaded one by one.
downloadQueue([...downloadQueue, { id, data }])
}
useEffect(() => {
downloadQueue.map(async(data) => {
await myDownloadAsyncFunction()
})
}, [downloadQueue]) // here the issue is whenever the downloadQueue updates it just start downloading without waiting for the previous one
Kindly help
CodePudding user response:
How about this? It uses Promise-chaining to make a simple queue
import React, { useState } from "react";
// Any async function or function that returns a promise
async function myDownloadAsyncFunction(data) {
return fetch('https://mydomain.test/path');
}
function DownloadButton() {
const [queue, setQueue] = useState(Promise.resolve());
onClickDownload = () => {
setQueue(queue
.then(() => myDownloadAsyncFunction('My data'))
.catch((err) => {console.error(err)})
)
}
return (
<button onClick={onClickDownload}>Download</button>
);
}
The above doesn't trigger the download from useEffect
. If you do want to use useEffect
, I think the state would probably need to be a counter to cause useEffect
to run when it changes:
import React, { useState, useEffect, useRef } from "react";
// Any async function or function that returns a promise
async function myDownloadAsyncFunction(data) {
return fetch('https://mydomain.test/path');
}
function DownloadButton() {
const queue = useRef(Promise.resolve());
const [clickCount, setClickCount] = useState(0);
useEffect(() => {
if (clickCount == 0) return;
queue.current = queue.current
.then(() => myDownloadAsyncFunction('My data'))
.catch((err) => {console.error(err)});
}, [clickCount]);
function onClickDownload() {
setClickCount(clickCount 1);
}
return (
<button onClick={onClickDownload}>Download</button>
);
}
Note in both of the above examples might need to get more complicated to better deal with some of the downloads failing. In these examples though, if a download fails, the next one should continue to attempt to be downloaded.
CodePudding user response:
A simple way is :
const [downloading,setDownloading] = useState(false)
useEffect( ()=> {
if(downloadQueue.length && !downloading){
myDownloadAsyncFunction(downloadQueue[0].data).then(()=>{
setDownloading(false)
setDownLoadQueue((a) => a.slice(1))
})
}
},[downloadQueue])
From your code, I'm not sure how do you pass parameter to myDownloadAsyncFunction()
. Here I juse assume that it use downloadQueue[0].data
.