Skip to content
Reactivity

useManualHistory

A hook for manually managing Observable change history. It only records a snapshot when commit() is called, and allows navigating previous states via undo/redo. Useful when auto-tracking is not needed, or when you want to record history only on explicit "save" actions.

Counter draft
Saved

Increment or decrement first, then decide when that state deserves a place in history.

0
Saved timeline

Newest snapshot is pinned at the top.

#1
0
import {
import useManualHistory
useManualHistory
,
import useObservable
useObservable
} from "@usels/web";
function
function Component(): JSX.Element
Component
() {
const
const counter$: any
counter$
=
import useObservable
useObservable
(0);
const {
const commit: any
commit
,
const undo: any
undo
,
const redo: any
redo
,
const canUndo$: any
canUndo$
,
const canRedo$: any
canRedo$
} =
import useManualHistory
useManualHistory
(
const counter$: any
counter$
);
return (
<
JSX.IntrinsicElements.div: DetailedHTMLProps<HTMLAttributes<HTMLDivElement>, HTMLDivElement>
div
>
<
JSX.IntrinsicElements.button: DetailedHTMLProps<ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement>
button
DOMAttributes<HTMLButtonElement>.onClick?: MouseEventHandler<HTMLButtonElement> | undefined
onClick
={() => {
const counter$: any
counter$
.
any
set
((
v: any
v
) =>
v: any
v
+ 1);
const commit: any
commit
(); }}>Save</
JSX.IntrinsicElements.button: DetailedHTMLProps<ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement>
button
>
<
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
>
);
}
import { useManualHistory, useObservable } from "@usels/web";
function Component() {
const value$ = useObservable(0);
// Keep at most 10 undo steps; older records are discarded automatically
const { commit, undo } = useManualHistory(value$, { capacity: 10 });
}

Use dump and parse to store a compact or transformed representation instead of raw value clones.

import { useManualHistory, useObservable } from "@usels/web";
function Component() {
const items$ = useObservable<string[]>([]);
const { commit, undo, history$ } = useManualHistory(items$, {
// Store as comma-separated string to save memory
dump: (arr) => arr.join(","),
parse: (str) => (str ? str.split(",") : []),
});
}

reset() restores the source to the last committed value without touching the undo/redo stacks.

import { useManualHistory, useObservable } from "@usels/web";
function Component() {
const text$ = useObservable("saved");
const { commit, reset } = useManualHistory(text$);
}
import { useManualHistory, useObservable } from "@usels/web";
function Component() {
const value$ = useObservable(0);
const { commit, clear, history$ } = useManualHistory(value$);
}
ParameterTypeDescription
source$Observable<Raw>- The Observable to track.
optionsManualHistoryOptions<Raw, Serialized> (optional)- Configuration for capacity, cloning, and serialization.
OptionTypeDefaultDescription
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.

ManualHistoryReturn<Raw, Serialized>

NameTypeDescription
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