Add Form-level debounce
All checks were successful
Lint / lint (push) Successful in 12s

This commit is contained in:
Julien Valverdé
2025-10-02 17:37:59 +02:00
parent 5951a0bf16
commit 8ce76fadb3

View File

@@ -15,14 +15,15 @@ export interface Form<in out A, in out I = A, out R = never, in out SA = void, i
extends Pipeable.Pipeable {
readonly [FormTypeId]: FormTypeId
readonly schema: Schema.Schema<A, I, R>,
readonly submit: (value: NoInfer<A>) => Effect.Effect<SA, SE, SR>,
readonly schema: Schema.Schema<A, I, R>
readonly submit: (value: NoInfer<A>) => Effect.Effect<SA, SE, SR>
readonly debounce: Option.Option<Duration.DurationInput>
readonly valueRef: SubscriptionRef.SubscriptionRef<Option.Option<A>>,
readonly encodedValueRef: SubscriptionRef.SubscriptionRef<I>,
readonly errorRef: SubscriptionRef.SubscriptionRef<Option.Option<ParseResult.ParseError>>,
readonly valueRef: SubscriptionRef.SubscriptionRef<Option.Option<A>>
readonly encodedValueRef: SubscriptionRef.SubscriptionRef<I>
readonly errorRef: SubscriptionRef.SubscriptionRef<Option.Option<ParseResult.ParseError>>
readonly validationFiberRef: SubscriptionRef.SubscriptionRef<Option.Option<Fiber.Fiber<void, never>>>
readonly submitStateRef: SubscriptionRef.SubscriptionRef<AsyncData.AsyncData<SA, SE>>,
readonly submitStateRef: SubscriptionRef.SubscriptionRef<AsyncData.AsyncData<SA, SE>>
readonly canSubmitSubscribable: Subscribable.Subscribable<boolean>
}
@@ -34,6 +35,7 @@ extends Pipeable.Class() implements Form<A, I, R, SA, SE, SR> {
constructor(
readonly schema: Schema.Schema<A, I, R>,
readonly submit: (value: NoInfer<A>) => Effect.Effect<SA, SE, SR>,
readonly debounce: Option.Option<Duration.DurationInput>,
readonly valueRef: SubscriptionRef.SubscriptionRef<Option.Option<A>>,
readonly encodedValueRef: SubscriptionRef.SubscriptionRef<I>,
@@ -53,7 +55,8 @@ export namespace make {
export interface Options<in out A, in out I, out R, in out SA = void, in out SE = A, out SR = never> {
readonly schema: Schema.Schema<A, I, R>
readonly initialEncodedValue: NoInfer<I>
readonly submit: (value: NoInfer<A>) => Effect.Effect<SA, SE, SR>
readonly submit: (value: NoInfer<A>) => Effect.Effect<SA, SE, SR>,
readonly debounce?: Duration.DurationInput,
}
}
@@ -72,6 +75,7 @@ export const make: {
return new FormImpl(
options.schema,
options.submit,
Option.fromNullable(options.debounce),
valueRef,
yield* SubscriptionRef.make(options.initialEncodedValue),
@@ -108,7 +112,10 @@ export const make: {
export const run = <A, I, R, SA, SE, SR>(
self: Form<A, I, R, SA, SE, SR>
): Effect.Effect<void, never, Scope.Scope | R> => Stream.runForEach(
self.encodedValueRef.changes,
self.encodedValueRef.changes.pipe(
Option.isSome(self.debounce) ? Stream.debounce(self.debounce.value) : identity
),
encodedValue => self.validationFiberRef.pipe(
Effect.andThen(Option.match({
onSome: Fiber.interrupt,
@@ -268,7 +275,7 @@ export const useForm: {
options: make.Options<A, I, R, SA, SE, SR>,
deps: React.DependencyList,
) {
const form = yield* Hooks.useMemo(() => make(options), deps)
const form = yield* Hooks.useMemo(() => make(options), [options.debounce, ...deps])
yield* Hooks.useFork(() => run(form), [form])
return form
})