React involved
Even values that need no hook can now require one.
React 19 ready, emotionally questionable
Hooks for problems nobody should have.
Technically functional React 19 hooks with reasons to exist ranging from “barely defensible” to “please just write the value directly.”
const status = useStateWithoutSetter("initial");
// There is no setter.
// status remains "initial".
// React has been involved for no reason.
Bad ideas, accurate naming
Every public hook works. Whether any of them should be called is a separate and much shorter discussion.
Even values that need no hook can now require one.
TypeScript can describe every questionable decision precisely.
Constants, browser melodrama, and intervals coexist without explanation.
Your bundler can remove the hooks you were sensible enough not to import.
More indirection, no benefit
Write 0. Use the value directly. Keep the setter. These are all excellent ways to avoid this package.
Read the fine print
Every public export, including the alternatives you should use instead.
Runs a callback repeatedly. This one is suspiciously defensible.
useInterval(callback: () => void, delay: number | null);
const [tick, setTick] = useState(0);
useInterval(() => setTick((n) => n + 1), 1000);
Calls pointer inactivity a dramatic pause because “idle” lacked theatre.
useDramaticPause(delay?: number): boolean;
const dramatic = useDramaticPause(1000);
Makes a browser tab resent visitors who briefly leave it.
useTabJealousy(options?: UseTabJealousyOptions): string | null;
const complaint = useTabJealousy();
Writes an alibi for a burst of keyboard nonsense.
useKeyMashExcuse(options?: UseKeyMashExcuseOptions): string | null;
const excuse = useKeyMashExcuse({ threshold: 4 });
Predicts absolutely nothing from how far someone scrolls.
useDoomScrollOracle(options?: UseDoomScrollOracleOptions): string | null;
const prophecy = useDoomScrollOracle({ distance: 800 });
Gives every focus change an entirely silent fanfare.
useFocusFanfare(options?: UseFocusFanfareOptions): string | null;
const fanfare = useFocusFanfare();
Treats every copy event as evidence of an unspecified crime.
useClipboardSuspicion(options?: UseClipboardSuspicionOptions): string | null;
const suspicion = useClipboardSuspicion();
Judges history traversal without knowing whether it was Back or Forward.
useBackButtonRegret(options?: UseBackButtonRegretOptions): string | null;
const regret = useBackButtonRegret();
Returns 0 through React. A reasonable person would write 0.
useZeroZero(): number;
const zero = useZeroZero();
Returns the value supplied. Using the value directly remains available.
useSameSame<T>(value: T): T;
const same = useSameSame(value);
Creates state, discards its setter, and leaves the initial value trapped forever.
useStateWithoutSetter<T>(initialValue: T): T;
const permanentlyInitial = useStateWithoutSetter("initial");
A reasonable person would use the value directly, or keep the setter. This hook should not exist.
Stores a stable ref inside another stable ref, doubling the indirection without improving the value.
useRefRef<T>(initialValue: T): RefObject<RefObject<T>>;
const refRef = useRefRef("unnecessarily deep");
const value = refRef.current.current;
A reasonable person would use useRef(initialValue). This hook should not exist.
Schedules a React effect whose entire effect is having no effect.
useEffectWithoutEffect(): void;
useEffectWithoutEffect();
A reasonable person would do nothing at all. This hook should not exist.
Memoizes a function, then memoizes another function whose only job is calling the first one.
useCallbackCallback<Args extends unknown[], Return>(
callback: (...args: Args) => Return,
): (...args: Args) => Return;
const greetAgain = useCallbackCallback(
(name: string) => `Hello ${name}`,
);
A reasonable person would use useCallback once. This hook should not exist.
Returns true, but only after React has confirmed what was already true.
useTrueTrue(): boolean;
const stillTrue = useTrueTrue();
A reasonable person would write true. This hook should not exist.
Prepares a React transition, then discards the only function capable of starting it.
useTransitionWithoutTransition(): boolean;
const isNothingPending = useTransitionWithoutTransition();
A reasonable person would write false. This hook should not exist.
Demands a boolean choice, then returns the same value from either branch.
useEitherWay<T>(value: T): (either: boolean) => T;
const chooseAnswer = useEitherWay("same answer");
const answer = chooseAnswer(false);
A reasonable person would use the value directly. This hook should not exist.
Puts a value in a collection by itself, then counts the unsurprising single occurrence.
useSelfCount<T>(value: T): readonly [T, number];
const [answer, occurrences] = useSelfCount("alone");
A reasonable person would write [value, 1]. This hook should not exist.
Calls a callback twice, then throws away the second result with complete precision.
useWasteCall(): <T>(callback: () => T) => T;
const wasteCall = useWasteCall();
const firstResult = wasteCall(() => calculate());
A reasonable person would call the callback once. This hook should not exist.
Creates React state, throws away its value, and keeps only the means to update something nobody can read.
useSetterWithoutState<T>(
initialValue: T,
): Dispatch<SetStateAction<T>>;
const setInvisibleState = useSetterWithoutState("hidden");
setInvisibleState("still hidden");
A reasonable person would keep the state returned by useState, or do nothing. This hook should not exist.
Creates a reducer whose dispatch asks for change and receives the exact same state back.
useNoChangeReducer<T>(
initialValue: T,
): readonly [T, DispatchWithoutAction];
const [value, refuseChange] = useNoChangeReducer("unchanged");
refuseChange();
A reasonable person would use the initial value and offer no action. This hook should not exist.
Stores a value in a ref, ignores the ref, then wraps the current value in a fresh object.
useRefThenWrap<T>(value: T): { value: T };
const wrapped = useRefThenWrap("already available");
A reasonable person would write { value }. This hook should not exist.