Is your feature request related to a problem? Please describe.
useAsyncEffect is a hook designed to manage asynchronous side effects. However, in real-word scenarios involving asynchronous operations such as fetch, it is crucial to abort ongoing operations when a component unmounts or when dependencies change.
Currently, to handle this properly, developers must manually create an AbortController and call abort() within the useAsyncEffect like this:
useAsyncEffect(async () => {
const abortController = new AbortController();
const data = await fetchData({ signal: abortController.signal });
setData(data);
return () => {
abortController.abort(reason);
console.log('Cleanup on unmount or dependencies change');
};
}, [dependencies]);
This approach introduces several problems
- Repeatedly write boilerplate code to manage the
AbortController
- A potential risk of missing abort handling
Describe the solution you'd like
I propose enhancing useAsyncEffect so that it automatically creates and manages an AbortController internally. This way, the developer simply receives the AbortSignal in the effect callback and passes it into any asynchronous operations that support aborting.
For example, using useAsyncEffect after this improvement would look like:
useAsyncEffect(async (signal) => {
const response = await fetch("/api/data", { signal });
const data = await response.json();
setData(data);
return () => {
console.log('Cleanup on unmount or dependencies change');
};
}, [dependencies], reason);
Benefits of this approach
- No need to manually create or manage AbortControllers
- Developers can write more declarative and cleaner asynchronous effects
- Prevents memory leaks and invalid state updates safely and consistently.
Describe alternatives you've considered
Here's the improved version:
export function useAsyncEffect(
effect: (signal: AbortSignal) => Promise<void | (() => void)>,
deps?: DependencyList,
reason: any,
) {
useEffect(() => {
const abortController = new AbortController();
const { signal } = abortController;
let cleanup: (() => void) | void;
effect(signal).then((result) => {
if (!signal.aborted) {
cleanup = result;
}
});
return () => {
abortController.abort(reason);
cleanup?.();
};
}, deps);
}
Additional context
AbortController and AbortSignal are widely supported in all modern browser. Reference, https://developer.mozilla.org/en-US/docs/Web/API/AbortController.
Is your feature request related to a problem? Please describe.
useAsyncEffectis a hook designed to manage asynchronous side effects. However, in real-word scenarios involving asynchronous operations such asfetch, it is crucial to abort ongoing operations when a component unmounts or when dependencies change.Currently, to handle this properly, developers must manually create an
AbortControllerand callabort()within theuseAsyncEffectlike this:This approach introduces several problems
AbortControllerDescribe the solution you'd like
I propose enhancing
useAsyncEffectso that it automatically creates and manages anAbortControllerinternally. This way, the developer simply receives theAbortSignalin theeffectcallback and passes it into any asynchronous operations that support aborting.For example, using
useAsyncEffectafter this improvement would look like:Benefits of this approach
Describe alternatives you've considered
Here's the improved version:
Additional context
AbortController and AbortSignal are widely supported in all modern browser. Reference, https://developer.mozilla.org/en-US/docs/Web/API/AbortController.