use-abortable-effect
Super simple React hook for running abortable effects based on the AbortController
API.
Check the live DEMO.
Interested in working on projects like this? Close is looking for great engineers to join our team!
Install
yarn add @closeio/use-abortable-effect
Benefits
- Extremely lightweight (less than 500B minzipped).
- It uses the
AbortController
API and it is compatible with the fetch
API. - If a browser does not support the
AbortController
API then the hook behaves exactly like a regular useEffect
hook. See Can I Use for browser support overview. - No other 3rd-party dependencies.
API differences over useEffect
- API is compatible with
useEffect
,
where the effect function you pass-in accepts an AbortSignal
instance as a param and you
can return a cleanup function that accepts an AbortController
instance. - Supports abortable
fetch
requests. - Supports running custom operations/computations that can be easily aborted.
- Auto-aborts effects on re-run (or component unmount), unless you provide
a custom cleanup function.
useEffect(() => {
return () => {
};
}, [deps]);
const abortControllerRef = useAbortableEffect(
(abortSignal) => {
return (abortController) => {
};
},
[deps],
);
Usage
Abortable fetch
requests
import React from 'react';
import useAbortableEffect from '@closeio/use-abortable-effect';
export default function MyAbortableFetchComponent() {
const abortControllerRef = useAbortableEffect((abortSignal) =>
fetch(url, { signal: abortSignal })
.then()
.catch((rejection) => {
if (rejection.name !== 'AbortError') {
return Promise.reject(rejection);
}
}),
);
const handleManualAbort = () => abortControllerRef.current.abort();
}
Arbitrary computation that can be aborted
import React from 'react';
import useAbortableEffect from '@closeio/use-abortable-effect';
export default function MyAbortableComputationComponent() {
const abortControllerRef = useAbortableEffect(abortSignal => {
new Promise((resolve, reject) => {
const abortRejection = new DOMException(
'Calculation aborted by the user',
'AbortError',
);
if (abortSignal.aborted) {
return reject(abortRejection);
}
const timeout = setTimeout(() => resolve(1), 5000);
abortSignal.addEventListener('abort', () => {
clearTimeout(timeout);
reject(abortRejection);
});
})
.then()
.catch(rejection => {
if (rejection.name !== 'AbortError') {
return Promise.reject(rejection);
}
}),
});
const handleManualAbort = () => abortControllerRef.current.abort();
}
Custom cleanup function
import React from 'react';
import useAbortableEffect from '@closeio/use-abortable-effect';
export default function MyCustomCleanupComponent() {
const [gotAborted, setGotAborted] = useState(false);
const abortControllerRef = useAbortableEffect((abortSignal) => {
fetch(url, { signal: abortSignal })
.then()
.catch((rejection) => {
if (rejection.name !== 'AbortError') {
return Promise.reject(rejection);
}
});
return (controller) => {
controller.abort();
setGotAborted(true);
};
});
const handleManualAbort = () => abortControllerRef.current.abort();
}
License
MIT © Close