Skip to content
Reactivity

useDataHistory

A hook that automatically tracks changes to an Observable and manages undo/redo history. Records a snapshot automatically whenever the source Observable changes. Built on top of useManualHistory, with additional support for auto-commit, pause/resume, and transaction.

Live editor
Tracking on

Every change commits immediately while tracking is active.

History timeline

Newest snapshot is pinned at the top.

#1
""
import {
import useDataHistory
useDataHistory
,
import useObservable
useObservable
} from "@usels/web";
function
function Component(): JSX.Element
Component
() {
const
const text$: any
text$
=
import useObservable
useObservable
("hello");
const {
const undo: any
undo
,
const redo: any
redo
,
const canUndo$: any
canUndo$
,
const canRedo$: any
canRedo$
} =
import useDataHistory
useDataHistory
(
const text$: any
text$
);
return (
<
JSX.IntrinsicElements.div: DetailedHTMLProps<HTMLAttributes<HTMLDivElement>, HTMLDivElement>
div
>
<
JSX.IntrinsicElements.input: DetailedHTMLProps<InputHTMLAttributes<HTMLInputElement>, HTMLInputElement>
input
InputHTMLAttributes<HTMLInputElement>.value?: string | number | {} | undefined
value
={
const text$: any
text$
.
any
get
()}
InputHTMLAttributes<HTMLInputElement>.onChange?: ChangeEventHandler<HTMLInputElement, HTMLInputElement> | undefined
onChange
={(
e: ChangeEvent<HTMLInputElement, HTMLInputElement>
e
) =>
const text$: any
text$
.
any
set
(
e: ChangeEvent<HTMLInputElement, HTMLInputElement>
e
.
ChangeEvent<HTMLInputElement, HTMLInputElement>.target: EventTarget & HTMLInputElement
target
.
any
value
)} />
<
JSX.IntrinsicElements.button: DetailedHTMLProps<ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement>
button
DOMAttributes<HTMLButtonElement>.onClick?: MouseEventHandler<HTMLButtonElement> | undefined
onClick
={
const undo: any
undo
}
ButtonHTMLAttributes<HTMLButtonElement>.disabled?: boolean | undefined
disabled
={!
const canUndo$: any
canUndo$
.
any
get
()}>Undo</
JSX.IntrinsicElements.button: DetailedHTMLProps<ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement>
button
>
<
JSX.IntrinsicElements.button: DetailedHTMLProps<ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement>
button
DOMAttributes<HTMLButtonElement>.onClick?: MouseEventHandler<HTMLButtonElement> | undefined
onClick
={
const redo: any
redo
}
ButtonHTMLAttributes<HTMLButtonElement>.disabled?: boolean | undefined
disabled
={!
const canRedo$: any
canRedo$
.
any
get
()}>Redo</
JSX.IntrinsicElements.button: DetailedHTMLProps<ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement>
button
>
</
JSX.IntrinsicElements.div: DetailedHTMLProps<HTMLAttributes<HTMLDivElement>, HTMLDivElement>
div
>
);
}

pause / resume — stop and restart auto-tracking

Section titled “pause / resume — stop and restart auto-tracking”
import { useDataHistory, useObservable } from "@usels/web";
function Component() {
const text$ = useObservable("hello");
const { pause, resume, isTracking$ } = useDataHistory(text$);
}

transaction — group mutations into one record

Section titled “transaction — group mutations into one record”

Multiple changes inside transaction() are recorded as a single undo step. Call the provided cancel() to abort the commit entirely.

import { useDataHistory, useObservable } from "@usels/web";
function Component() {
const value$ = useObservable(0);
const { transaction, undo } = useDataHistory(value$);
}

Return false from shouldCommit to skip recording specific values.

import { useDataHistory, useObservable } from "@usels/web";
function Component() {
const count$ = useObservable(0);
// Only record even numbers
const { undo } = useDataHistory(count$, {
shouldCommit: (value) => value % 2 === 0,
});
}
import { useDataHistory, useObservable } from "@usels/web";
function Component() {
const text$ = useObservable("");
// Keep at most 50 undo steps
const { undo, redo } = useDataHistory(text$, { capacity: 50 });
}
ParameterTypeDescription
source$Observable<Raw>- The Observable to track.
optionsDataHistoryOptions<Raw, Serialized> (optional)- Configuration for auto-tracking, filtering, and serialization.
OptionTypeDefaultDescription
eventFilterEventFilter-Custom EventFilter to control when auto-commits fire. Used internally by throttledHistory / debouncedHistory.
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