Skip to content

useThrottledValue

Edit

Publishes the latest generic value at most once during each interval.

useThrottledValue.ts
import { useEffect, useRef, useState } from "react";
/** Returns a value that updates at most once during each interval. */
export const useThrottledValue = <Value>(
value: Value,
intervalMs: number,
): Value => {
if (!Number.isFinite(intervalMs) || intervalMs < 0) {
throw new RangeError("intervalMs must be a finite, non-negative number");
}
const [throttledValue, setThrottledValue] = useState<Value>(() => value);
const lastUpdateRef = useRef(Date.now());
useEffect(() => {
if (Object.is(value, throttledValue)) return;
const now = Date.now();
const elapsed = now - lastUpdateRef.current;
if (elapsed < 0 || elapsed >= intervalMs) {
lastUpdateRef.current = now;
setThrottledValue(() => value);
return;
}
const timer = setTimeout(() => {
lastUpdateRef.current = Date.now();
setThrottledValue(() => value);
}, intervalMs - elapsed);
return () => {
clearTimeout(timer);
};
}, [intervalMs, throttledValue, value]);
return throttledValue;
};
Terminal
wget -O src/hooks/useThrottledValue.ts https://raw.githubusercontent.com/jrTilak/lazykit/HEAD/registry/react-hooks/useThrottledValue.ts
useThrottledValue.example.tsx
import { useState } from "react";
import { useThrottledValue } from "./useThrottledValue";
export const ThrottledRange = () => {
const [value, setValue] = useState(0);
const displayedValue = useThrottledValue(value, 250);
return (
<label>
Value: {displayedValue}
<input
type="range"
min="0"
max="100"
value={value}
onChange={(event) => setValue(event.currentTarget.valueAsNumber)}
/>
</label>
);
};
  • value (T) — Value to throttle.
  • intervalMs (number) — Finite, non-negative minimum interval between published changes.

T — The initial value immediately and the latest pending value when the current throttle window ends. Function values are stored as values and are never invoked by the hook.