Kanzo UI
Hooks

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

  • flush on 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 what onCommit answered, so when onCommit answers the write — (next) => save.mutateAsync(next) — the caller can wait on the write its decision made, as AnswerCard's Add to the dashboard does. A commit after the pause or on flush has nobody to hand the promise to, so the owner reports a failed write, as every write does (a mutation's onError); 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; draft keeps 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, where flush and 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

NameTypeWhat it is
draftTWhat the control shows.
change(next: T) => voidA keystroke: updates draft now, commits after delay.
flush() => voidCommits a pending draft now.
commit(next: T) => RCommits next now and drops anything pending; answers what onCommit answered.
cancel() => voidDrops anything pending without committing it.

On this page