Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 | 15x 15x 8x 8x 15x 13x 8x 8x 5x | import { useEffect, useRef, type DependencyList, type EffectCallback } from "react";
/**
* A drop-in replacement for `useEffect` that skips running on the first render
* and only fires on subsequent updates (when the dependencies change).
*
* Handy for reacting to changes without also running the effect on mount — e.g.
* syncing a value to storage only after the user changes it, or firing an
* `onChange` without an initial spurious call.
*
* Remount-safe: a dedicated mount-tracking effect resets the flag on unmount,
* so it correctly skips again after a React StrictMode dev remount
* (mount → unmount → remount) or any real remount, instead of firing on mount.
*
* @param effect - Effect callback (may return a cleanup function)
* @param deps - Dependency list, same semantics as `useEffect`
*
* @example
* ```tsx
* useUpdateEffect(() => {
* // does NOT run on mount, only when `query` later changes
* analytics.track("search", { query });
* }, [query]);
* ```
*/
export function useUpdateEffect(
effect: EffectCallback,
deps?: DependencyList
): void {
const isMounted = useRef(false);
// Reset on unmount so a remount (incl. StrictMode's dev remount) is treated
// as a fresh mount and its first run is skipped again. React flushes all
// cleanups before all setups, so `isMounted` is `false` when the deps effect
// below re-runs on remount.
useEffect(() => {
return () => {
isMounted.current = false;
};
}, []);
useEffect(() => {
if (!isMounted.current) {
isMounted.current = true;
return;
}
return effect();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, deps);
}
|