In a React app, I would like to have a function initiate a series of async calls and then have the ability to alter the state available to those calls as they run. As an example, I would let the user initiate a retrieval of 5 data files, which could run in the background and take minutes, but give them the option to abort the process or trim the total file count.
Here's an idea of what it could look like, but unfortunately this pattern doesn't seem to work:
function App() {
const [halt, setHalt] = useState(false);
return (
...
<button onClick={() => longProcess(halt)}>Start</button>
<button onClick={() => setHalt(true)}>Stop</button>
...
);
}
async function longProcess(halt) {
for (const fileid of files_to_get) {
// For example, halt if the user clicks the Stop button during execution
if (halt) break;
await getDataFile(fileid);
}
}
Ideally, I want to use pure functional components and to allow the async function to be available for use by multiple components. So I've been using React Hooks across the board. I have come up with 3 solutions, but none of them quite fit the bill:
this.state will update asynchronously
useRef() is a suggested option
I'd be curious if there's any clean way similar to the 3rd example, that I just haven't come across in my limited React experience. Other suggestions welcome as well!
To have a reusable function I would define it inside a Hook.
The following proposal uses useState to execute the function. We need useState to trigger a render when the value changes. This value will call the function from inside a useEffect.
It also uses useRef so that the process can start and later read its value, that could have changed during execution.
const App = () => {
const { startProcess, stopProcess } = useLongProcess();
return (
<Fragment>
<button onClick={startProcess}>Start</button>
<button onClick={stopProcess}>Stop</button>
</Fragment>
);
};
const useLongProcess = () => {
const stop = useRef(false);
const [start_process, setStartProcess] = useState(false);
useEffect(() => {
if (!start_process) {
return;
}
const longProcess = async () => {
for (const fileid of files_to_get) {
if (stop.current) break;
await getDataFile(fileid);
}
};
longProcess();
}, [start_process]);
return {
startProcess: () => setStartProcess(true),
stopProcess: () => {
stop.current = true;
}
};
};
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With