# debounce

> `debounce: 300` tells a control to wait for a pause before it runs its event handler. Without it, an `onInput` handler fires on every keystroke — so a handler that asks the server a question would make one round-trip per character. With it, you get one, when the user stops typing.

<!-- id: ui-debounce · area: ui · stability: stable · html: https://osysharp.com/reference/ui/debounce/ -->

## Summary        {#summary}
`debounce:` is a property of the control, written on the call. It coalesces that control's event handlers: the handler
runs once, after the events stop for the given number of milliseconds.

```osy syntax
Input(value: draft.Slug, onInput: CheckSlug, debounce: 300);
```

Type `acme` quickly and `CheckSlug` runs **once**, 300ms after the last keystroke — not four times.

## Signature      {#signature}
```osy syntax
debounce: <milliseconds>      // a non-negative whole number; applies to this element's event handlers
```

- It needs an event handler to coalesce (`onInput`, `onClick`, …). A `debounce:` with no handler is a compile error —
  it would do nothing, silently.
- The handler runs with the **latest** value: the field's binding is written before the handler runs, so a handler
  that reads the field it is bound to sees what the user just typed.

## Description    {#description}
Some handlers are cheap and want to fire on every event. Some ask a question that costs a round-trip — *is this name
available?*, *what matches this search?* — and firing those per keystroke is wasteful and racy. `debounce:` is how you
say "wait until they stop."

**What debounce does not do.** It does not deduplicate answers. If a handler asks the server something, a slow reply
for an earlier value can still land after a faster reply for a later one. When the answer is written into state, guard
it: re-read the field after the call and drop the answer if it no longer describes what's in the box (the example
below does this). Debounce reduces the number of questions; it does not order the answers.

**It is not validation.** A friendly as-you-type check is a courtesy. The rules that actually protect your data are the
ones declared on the entity — `[Unique]`, `[Required]`, `[Pattern]` — and those are enforced when the data is saved,
whatever the client did or didn't check first.

## Examples       {#examples}
The is-this-name-taken check. An ordinary server function answers the question; an ordinary action asks it and writes
the answer into state; `debounce:` makes it one round-trip per pause in typing:

```osy title="slug-availability" test app=ui-debounce
entity Organization {
  [Required, MaxLength(100), Unique, Pattern("^[a-z0-9]+(-[a-z0-9]+)*$")] string Slug;
  string Name;
}

// An ordinary server function — it just answers a question.
bool CheckSlugAvailability(string candidate) {
  if (candidate == "") { return true; }
  return !Organization.Any(o => o.Slug == candidate);
}

[Page("/org/new")]
[Render(CSR)]
component OrgCreatePage() {
  Organization draft;
  bool slugFree = true;
  on mount { draft = new Organization {}; }

  // An action may write state and may call a server function. Re-reading the field after the call drops a stale
  // answer — the reply for a slug the user has already typed past.
  action CheckSlug() {
    var candidate = draft.Slug;
    var free = CheckSlugAvailability(candidate);
    if (candidate == draft.Slug) { slugFree = free; }
  }

  render {
    Stack(gap: 4) {
      Input(value: draft.Name, placeholder: "Organization name");
      Input(value: draft.Slug, placeholder: "team-slug", onInput: CheckSlug, debounce: 300);
      if (!slugFree) { Text("That slug is taken."); }
    }
  }
}
```

A search box wants the same thing — one query per pause, not one per letter:

```osy title="a search box — one query per pause, not one per letter" syntax
Input(value: term, onInput: Search, debounce: 250);
```

## See also       {#see-also}
- [component](https://osysharp.com/reference/ui/component/) — `action` members, and why an action (not an `on change` block) is what asks a question like this.
- [control — foreign UI controls (charts, grids, maps)](https://osysharp.com/reference/ui/controls/) — the controls an event handler can hang off, and their event props.
- [creating & saving data](https://osysharp.com/reference/ui/data-mutation/) — binding an input to an entity field, and `UnitOfWork.Commit()`.
