refactor: use Atom.debounce for validation and auto-submit debouncing#100
Merged
Conversation
Replace the two hand-rolled setTimeout/clearTimeout debounces in FormAtoms
with compositions over Atom.debounce, and keep the debounce duration as
Duration.Input end-to-end in ParsedMode instead of eagerly collapsing to
millis (Atom.debounce accepts Duration.Input directly).
Preserved semantics:
- Validation debounces only for onChange + positive debounce + not
autoSubmit; zero/absent debounce still validates synchronously.
- shouldValidate is evaluated when the debounced value lands, and the
onBlur blur-triggered validation stays immediate (un-debounced).
- Value changes are debounced through a boxed { value } wrapper so a
change that reverts to the pre-burst value within the window still
triggers validation (Atom.debounce alone would drop it via Object.is).
- Auto-submit funnels submit requests through a counter atom debounced
with Atom.debounce; the in-flight pendingChanges queue is kept via the
existing submitAtom subscription, so a change during an in-flight
submit still yields exactly one debounced follow-up submit, and
reference-equal value updates still never submit.
Regression tests added first and verified against the previous
implementation: exact-count debounced validation for rapid changes,
revert-within-window validation, immediate validation for zero/absent
debounce and for autoSubmit mode, onBlur gating before touch, immediate
blur validation, exact-count debounced auto-submit, and the in-flight
follow-up submit queue.
lucas-barake
force-pushed
the
refactor/atom-debounce
branch
from
July 10, 2026 02:41
e84a690 to
a20d230
Compare
Merged
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Replaces the two hand-rolled
setTimeout/clearTimeoutdebounces inFormAtoms.tswith compositions over effect v4'sAtom.debounce, and keeps the debounce duration asDuration.Inputend-to-end inParsedModeinstead of eagerly collapsing it to millis inMode.parse(Atom.debounceacceptsDuration.Inputdirectly).How
triggerValidationAtom: when debouncing applies (onChange+ positive debounce + notautoSubmit), value changes flow throughAtom.debounceover a boxed{ value }wrapper ofvalueAtom, and the subscriber evaluatesshouldValidateAtomwhen the debounced value lands before firing the validation. The box matters:Atom.debouncedrops updates that areObject.is-equal to its current value, so debouncingvalueAtomdirectly would swallow a change that reverts to the pre-burst value within the window — the wrapper produces a fresh reference per change, preserving the previous behavior of validating in that case. The un-debounced path (zero/absent debounce,onBlur,onSubmitgating) and the immediate blur-triggered validation are unchanged.autoSubmitAtom: submit requests are funneled through a monotonically increasing counter atom debounced withAtom.debounce; every bump restarts the trailing window and the subscriber submits when it lands. The in-flight re-entrancy queue is intentionally kept as a subscription tosubmitAtomcompletion (it is not a debounce concern): a change during an in-flight submit markspendingChangesand yields exactly one debounced follow-up submit after completion, and reference-equal value updates still never submit. Zero/absent debounce still submits synchronously.Atom.debounceis built onAtom.transform, which already applies thesetIdleTTL(0)convention this file uses, and its finalizer clears any pending timer on unmount — matching the manual cleanup that was removed.Semantics
No intentional changes. One equivalence worth noting: the old code checked
shouldValidateAtomwhen scheduling the timer, the new code checks it when the debounced value lands. Debouncing only ever applies inonChangemode, whereshouldValidateAtomis constantlytrue, so the two are indistinguishable — and gate-at-fire-time is the more correct reading.Tests
Nine regression tests were added first and verified green against the previous implementation, then kept green through the refactor:
0autoSubmitis enabledonBlurmode (shouldValidategate)onBlurmodeMode.test.tsexpectations updated for theDuration.Inputpassthrough. Full suite: 310 tests passing,check:typesandlintclean.