In the following example, if the "slow" button is clicked immediately followed by a click of the "fast" button, dependsOnA will first be set to "Result from fast promise" since the fast promise resolves first. After about a second the slow promise will resolve and dependsOnA will be set to "Result from slow promise" . What I really want is for the state dependsOnA to always reflect the current state of a. I.e. dependsOnA should always get the value "Result from fast promise" when a is set to true and "Result from slow promise" when a set to false.
What is the best way of accomplishing this?
import * as React from 'react';
const slowPromise: () => Promise<string> = () => {
return new Promise((resolve) => {
setTimeout(() => resolve('Result from slow promise'), 2000);
});
};
const fastPromise: () => Promise<string> = () => {
return new Promise((resolve) => {
setTimeout(() => resolve('Result from fast promise'), 1000);
});
};
export function Example(props: {}) {
const [a, setA] = React.useState<boolean>();
const [dependsOnA, setDependsOnA] = React.useState<string>();
React.useEffect(() => {
if (a)
fastPromise().then(setDependsOnA);
else
slowPromise().then(setDependsOnA);
}, [a]);
return (
<div>
<button onClick={() => setA(false)}>Slow</button>
<button onClick={() => setA(true)}>Fast</button>
{dependsOnA}
</div>
);
}
I came up with the following solution which uses the cleanup callback of the useEffect hook to "cancel" the state update after the promise has resolved if a has changed since the promise was created. However, this solution feels a bit hacky.
React.useEffect(() => {
let isCanceled: boolean = false;
if (a)
fastPromise().then((res) => {
if (!isCanceled) setDependsOnA(res);
});
else
slowPromise().then((res) => {
if (!isCanceled) setDependsOnA(res);
});
return () => { isCanceled = true; };
}, [a]);