Why
Work started from an event handler outlives the component. A request, listener or timer keeps running after unmount and can update stale state.
These hooks give you an AbortSignal tied to the component's lifetime. They are safe under React Strict Mode.
Install
npm install use-is-mounted-ref
Requires React 16.8 or later. CI tests React 18 and 19.
Quick start
import { useAbortSignal } from 'use-is-mounted-ref';
function SaveButton({ data }) {
const signal = useAbortSignal();
const save = () =>
fetch('/api/save', { method: 'POST', body: JSON.stringify(data), signal }).catch((err) => {
if (err.name !== 'AbortError') throw err;
});
return <button onClick={save}>Save</button>;
}
If the component unmounts mid-request, the request is aborted.
API
| Hook | Returns | Aborts / flips when |
|---|---|---|
useAbortSignal() |
AbortSignal |
Component unmounts |
useAbortController() |
AbortController |
Component unmounts |
useIsMountedRef() |
{ readonly current: boolean } |
Component unmounts |
All three return a stable value across renders.
useAbortSignal
Use it when you only need the signal. This covers most cases.
useAbortController
Use it when you also need to call abort() yourself, for example from a Cancel button.
Example: cancel button
import { useAbortController } from 'use-is-mounted-ref';
function Upload({ file }) {
const controller = useAbortController();
const upload = () => fetch('/api/upload', { method: 'POST', body: file, signal: controller.signal });
return (
<>
<button onClick={upload}>Upload</button>
<button onClick={() => controller.abort()}>Cancel</button>
</>
);
}
Once aborted, the controller stays aborted until the component remounts.
useIsMountedRef
Use it for promises that cannot take a signal, such as some third-party SDKs.
Example: guard a non-abortable promise
import { useState } from 'react';
import { useIsMountedRef } from 'use-is-mounted-ref';
function Profile({ sdk }) {
const isMountedRef = useIsMountedRef();
const [user, setUser] = useState(null);
const load = async () => {
const result = await sdk.getUser();
if (isMountedRef.current) setUser(result);
};
return <button onClick={load}>{user ? user.name : 'Load'}</button>;
}
Inside useEffect
These hooks abort only on unmount, not when effect dependencies change. For work keyed on props, create a controller per effect run:
useEffect(() => {
const controller = new AbortController();
fetch(`/api/users/${id}`, { signal: controller.signal });
return () => controller.abort();
}, [id]);
Docs
Contributing
See CONTRIBUTING.md. Report bugs or suggest ideas in issues.