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.