useDebouncedCommit
A control that keeps what is being typed to itself and tells its owner once typing pauses.
0 writes
Usage
import { useDebouncedCommit } from "@kanzo-tech/ui";const { draft, change, flush } = useDebouncedCommit(value, onChange);
<Input value={draft} onChange={(e) => change(e.target.value)} onBlur={flush} />For an owner that is expensive to write to — a form model that revalidates, a request, a document
that re-renders — and a control that would otherwise write on every keystroke. The control shows
draft, which is the last thing typed; the owner hears the last one, after the pause.
draft follows value whenever nothing is pending, so a change made elsewhere — a reset, another
editor — still lands in the control, and one made while the user is typing does not overwrite the
word they are in the middle of.
Blur, picks, cancelling and unmount
flushon blur. It commits a pending draft at once and does nothing when there is none. This is what keeps the last characters from being lost when the control goes away before the timer fires.commit(next)for a pick. A choice made from a list supersedes typing: it commits now and drops anything pending. It answers whatonCommitanswered, so whenonCommitanswers the write —(next) => save.mutateAsync(next)— the caller can wait on the write its decision made, asAnswerCard's Add to the dashboard does. A commit after the pause or onflushhas nobody to hand the promise to, so the owner reports a failed write, as every write does (a mutation'sonError); the hook does not leave it unhandled.cancel()when the owner's own write supersedes the draft. It drops what is pending without committing it;draftkeeps showing it until the owner's value changes. A save that writes the same field itself — a dashboard store replacing the whole document — cancels and writes once, whereflushand then the write would be two writes whose result rests on their order.- Nothing is committed on unmount. A cleanup that commits also commits in Strict Mode's mount–unmount–mount, and the owner then hears a value nobody typed.
It is generic over the value, so a control that holds string | null or a number needs no
wrapper: whatever you pass as value is what change and commit take.
API Reference
useDebouncedCommit
function useDebouncedCommit<T, R = void>(
value: T,
onCommit: (next: T) => R,
delay?: number,
): DebouncedCommit<T, R>;delay is in milliseconds and defaults to 250.
Returns
| Name | Type | What it is |
|---|---|---|
draft | T | What the control shows. |
change | (next: T) => void | A keystroke: updates draft now, commits after delay. |
flush | () => void | Commits a pending draft now. |
commit | (next: T) => R | Commits next now and drops anything pending; answers what onCommit answered. |
cancel | () => void | Drops anything pending without committing it. |