Skip to content
Reactivity

useThrottledHistory

A hook that tracks Observable change history with throttle. A thin wrapper around useHistory with throttleFilter applied — for rapidly changing values like sliders or drag interactions, it records snapshots at fixed intervals instead of on every change.

Sampled slider
Up to 1 commit / 300ms

Drag aggressively and notice how the timeline keeps only periodic checkpoints.

Value: 50
Throttled timeline

Newest snapshot is pinned at the top.

#1
50
import {
import useObservable
useObservable
,
import useThrottledHistory
useThrottledHistory
} from "@usels/web";
function
function Component(): JSX.Element
Component
() {
const
const slider$: any
slider$
=
import useObservable
useObservable
(50);
// Record at most once every 300ms
const {
const undo: any
undo
,
const redo: any
redo
,
const canUndo$: any
canUndo$
} =
import useThrottledHistory
useThrottledHistory
(
const slider$: any
slider$
, {
throttle: number
throttle
: 300 });
return <
JSX.IntrinsicElements.input: DetailedHTMLProps<InputHTMLAttributes<HTMLInputElement>, HTMLInputElement>
input
InputHTMLAttributes<HTMLInputElement>.type?: HTMLInputTypeAttribute | undefined
type
="range"
InputHTMLAttributes<HTMLInputElement>.value?: string | number | {} | undefined
value
={
const slider$: any
slider$
.
any
get
()}
InputHTMLAttributes<HTMLInputElement>.onChange?: ChangeEventHandler<HTMLInputElement, HTMLInputElement> | undefined
onChange
={(
e: ChangeEvent<HTMLInputElement, HTMLInputElement>
e
) =>
const slider$: any
slider$
.
any
set
(+
e: ChangeEvent<HTMLInputElement, HTMLInputElement>
e
.
ChangeEvent<HTMLInputElement, HTMLInputElement>.target: EventTarget & HTMLInputElement
target
.
any
value
)} />;
}

By default both leading and trailing edges fire. Disable either to customize behavior.

import { useObservable, useThrottledHistory } from "@usels/web";
function Component() {
const value$ = useObservable(0);
// Only record on the trailing edge (end of throttle window)
const { undo } = useThrottledHistory(value$, {
throttle: 500,
leading: false,
trailing: true,
});
}
import { useObservable, useThrottledHistory } from "@usels/web";
function Component() {
const position$ = useObservable({ x: 0, y: 0 });
const { undo, redo } = useThrottledHistory(position$, {
throttle: 200,
capacity: 20, // keep at most 20 throttled snapshots
});
}
ParameterTypeDescription
source$Observable<Raw>- The Observable to track.
optionsThrottledHistoryOptions<Raw, Serialized> (optional)- Throttle timing and history configuration.
OptionTypeDefaultDescription
throttleMaybeObservable<number>200Throttle interval in milliseconds.
trailingbooleantrueFire on the trailing edge of the throttle window.
leadingbooleantrueFire on the leading edge of the throttle window.
shouldCommit((newValue: Raw) => boolean)-Gate function called before each auto-commit. Return false to skip recording the current value.
capacitynumberInfinityMaximum number of undo records to keep. When exceeded, the oldest records are discarded.
cloneboolean | ((value: Raw) => Raw)-Clone strategy for snapshots. - true (default): uses structuredClone - false: stores references (safe for primitives only) - (value) => cloned: custom clone function
dump((value: Raw) => Serialized)-Custom serializer: Raw → Serialized for storage. When provided, clone is ignored and parse must also be provided.
parse((value: Serialized) => Raw)-Custom deserializer: Serialized → Raw for restore. When provided, clone is ignored and dump must also be provided.

DataHistoryReturn<Raw, Serialized>

NameTypeDescription
isTracking$ReadonlyObservable<boolean>Whether auto-tracking is currently active.
pauseFnStop auto-committing.
resume(commitCurrent?: boolean | undefined) => voidRestart auto-committing. If commitCurrent is true, immediately commit current value.
transaction(fn: (cancel: Fn) => void) => voidGroup multiple mutations into a single history record. Call cancel() inside fn to abort.
canUndo$ReadonlyObservable<boolean>Whether undo is possible (undoStack is non-empty).
canRedo$ReadonlyObservable<boolean>Whether redo is possible (redoStack is non-empty).
history$ReadonlyObservable<UseHistoryRecord<Serialized>[]>Full history array, newest first: [current, previous, ...].
last$ReadonlyObservable<UseHistoryRecord<Serialized>>The most recent (current) history record.
commitFnRecord the current source value as a new history point. Clears redo stack.
undoFnRestore the previous committed value.
redoFnRe-apply the next value after an undo.
clearFnWipe all history and create a fresh initial record from current source.
resetFnRestore source to the last committed value without modifying stacks.

View on GitHub