diff --git a/packages/effect-fc/src/Form.ts b/packages/effect-fc/src/Form.ts index 580477b..e9c44ea 100644 --- a/packages/effect-fc/src/Form.ts +++ b/packages/effect-fc/src/Form.ts @@ -21,7 +21,7 @@ extends Pipeable.Pipeable { ): Subscribable.Subscribable useInput

>( - path: P + options: Form.useInput.Options ): Effect.Effect< Form.useInput.Result>, ParseResult.ParseError | NoSuchElementException, @@ -31,9 +31,14 @@ extends Pipeable.Pipeable { export namespace Form { export namespace useInput { - export interface Result { - readonly value: I - readonly setValue: React.Dispatch> + export interface Options> { + readonly path: P + readonly defaultValue?: PropertyPath.ValueFromPath, NoInfer

> + } + + export interface Result { + readonly value: T + readonly setValue: React.Dispatch> readonly issues: readonly ParseResult.ArrayFormatterIssue[] } } @@ -86,15 +91,24 @@ extends Pipeable.Class() implements Form { }, [this.latestValueRef, ...path]) } - useInput

>(path: P) { + useInput

>( + options: Form.useInput.Options + ) { const self = this return Effect.gen(function*() { const internalValueRef = yield* Hooks.useMemo(() => self.latestValueRef.pipe( - Effect.andThen(Schema.encode(self.schema)), - Effect.andThen(PropertyPath.get(path)), + Effect.andThen(flow( + Schema.encode(self.schema), + Effect.andThen(PropertyPath.get(options.path)), + Effect.catchTag("ParseError", e => options.defaultValue !== undefined && options.defaultValue !== null + ? Effect.succeed(options.defaultValue) + : Effect.fail(e) + ), + )), Effect.andThen(SubscriptionRef.make>), - ), [self.latestValueRef, ...path]) - const issuesSubscribable = self.useFieldIssuesSubscribable(path) + ), [self.latestValueRef, ...options.path]) + + const issuesSubscribable = self.useFieldIssuesSubscribable(options.path) const [value, setValue] = yield* Hooks.useRefState(internalValueRef) const [issues] = yield* Hooks.useSubscribe(issuesSubscribable) @@ -107,7 +121,7 @@ extends Pipeable.Class() implements Form { ), internalValue => self.latestValueRef.pipe( Effect.andThen(Schema.encode(self.schema)), - Effect.andThen(PropertyPath.immutableSet(path, internalValue)), + Effect.andThen(PropertyPath.immutableSet(options.path, internalValue)), Effect.andThen(flow( Schema.decode(self.schema), Effect.andThen(v => SubscriptionRef.set(self.latestValueRef, v)), @@ -115,7 +129,7 @@ extends Pipeable.Class() implements Form { Effect.catchTag("ParseError", e => SubscriptionRef.set(self.errorRef, Option.some(e))) )), ), - ), [internalValueRef, self.latestValueRef, self.schema, self.errorRef, ...path]) + ), [internalValueRef, self.latestValueRef, self.schema, self.errorRef, ...options.path]) return { value, setValue, issues } }) diff --git a/packages/example/src/routes/form.tsx b/packages/example/src/routes/form.tsx index 2cfb956..a25a09d 100644 --- a/packages/example/src/routes/form.tsx +++ b/packages/example/src/routes/form.tsx @@ -6,22 +6,22 @@ import { Component, Form } from "effect-fc" import { useContext, useFork } from "effect-fc/hooks" -const LoginFormSchema = Schema.Struct({ +const RegisterFormSchema = Schema.Struct({ email: Schema.String, password: Schema.String.pipe(Schema.minLength(3)), }) -class LoginForm extends Effect.Service()("LoginForm", { +class RegisterForm extends Effect.Service()("RegisterForm", { scoped: Form.make({ - schema: LoginFormSchema, - initialValue: { email: "", password: "xxx" }, + schema: RegisterFormSchema, + initialValue: { email: "", password: "" }, }) }) {} -class LoginFormComponent extends Component.makeUntraced(function* LoginFormComponent() { - const form = yield* LoginForm - const emailInput = yield* form.useInput(["email"]) - const passwordInput = yield* form.useInput(["password"]) +class RegisterPage extends Component.makeUntraced(function* RegisterPage() { + const form = yield* RegisterForm + const emailInput = yield* form.useInput({ path: ["email"], defaultValue: "" }) + const passwordInput = yield* form.useInput({ path: ["password"], defaultValue: "" }) yield* useFork(() => Stream.runForEach(form.latestValueSubscribable.changes, Console.log), []) @@ -63,15 +63,15 @@ class LoginFormComponent extends Component.makeUntraced(function* LoginFormCompo }) {} -const FormRoute = Component.makeUntraced(function* FormRoute() { - const context = yield* useContext(LoginForm.Default) - const LoginFormComponentFC = yield* Effect.provide(LoginFormComponent, context) +const RegisterRoute = Component.makeUntraced(function* RegisterRoute() { + const context = yield* useContext(RegisterForm.Default) + const RegisterRouteFC = yield* Effect.provide(RegisterPage, context) - return + return }).pipe( Component.withRuntime(runtime.context) ) export const Route = createFileRoute("/form")({ - component: FormRoute + component: RegisterRoute })