#custom reusable form.Field component, trying to use Form Composition

37 messages · Page 1 of 1 (latest)

grim hazel
#

Hello, I am reading the docs of Form Composition and I just saw the example of creating a form.Subscribe wrapper for a subscription button, so I guess that, for example, I could create a reusable component to be used for all my form.Field text inputs. This is my first attempt:

import type { ZodObject, ZodRawShape } from "zod";
import LabeledField from "@/components/form/labeled-field";
import { isFieldRequired, useFormContext } from "@/components/form/utils";
import { Input } from "@/components/ui/input";

interface FieldInputProps {
  id: string;
  fieldName: string;
  label: string;
  schema: ZodObject<ZodRawShape>;
}

export default function FieldInput({ id, fieldName, label, schema }: FieldInputProps) {
  const form = useFormContext();

  return (
    <form.Field
      name={fieldName}
      children={({ state, handleChange, handleBlur }) => (
        <LabeledField labelFor={id} label={label} required={isFieldRequired(fieldName, schema)}>
          <Input id={id} value={state.value ?? ""} onChange={(e) => handleChange(e.target.value)} onBlur={handleBlur} />
        </LabeledField>
      )}
    />
  );
}

However... The name prop shows Type 'string' is not assignable to type 'never'.ts(2322), and state is never too. Am I doing something wrong?

wise quest
#

the field components are meant for the UI side of things (the components in the field callback)

if you need to encapsulate logic as well, then field groups are the way to go

#

logic as in validators and dependent fields

grim hazel
wise quest
grim hazel
#

Hmmmmmm but I guess I want to use a formComponent, right?

wise quest
grim hazel
grim hazel
wise quest
#

not quite

#

it‘ll be a component accessed from the field within an AppField child

#
(

 <form.AppField
      name="firstName"
      children={(field) => <field.TextField label="First Name" />}
    />
  )

grim hazel
#

Oh okay, I think I got it, I am reading the docs again, but I still have the same question, could I use <form.Field> in a generic way in a field/form component? 😅

wise quest
#

at runtime, technically yes. Type-safe, absolutely not

#

it also merges the logic concern and ui concern into one component which is why the AppField is still externally written

grim hazel
wise quest
#

#

I wonder how many people missed that

#

by the way, are you migrating from rhf?

#

if so, I can give better pointers in the future since the syntax is quite different

grim hazel
wise quest
#

another form library that‘s common in React, React Hook Form

grim hazel
#

Ohh no no, I didn't know it

grim hazel
#

Hey @wise quest , after switching my components to custom form fields, now the issue of opening the dialog -> edit a field -> close the dialog -> open it again -> the value is still modified 🙁 is happening again 😭 Using a key for the form is not enough I guess because my source data is the in both cases. Shall I use form.reset? I would like to avoid it...

wise quest
#
<Container> // contains defaultValues
  <Dialog>
    <Form />
  </Dialog>
</Container>

Is it this?

wise quest
#

no need to give it actual values to reset to, since that's still handled by the code above

grim hazel
#

I think I was previously solving this issue using conditional rendering of the shadcn dialog, which is not a good practice, the dialog was instantly dissapearing and I was getting apollo query errors 😵‍💫

#

I am going to try to use form.reset instead of the defaultValues argument, I guess this way the form types will be inferred from the form.reset call instead, right?

wise quest
#

conditional rendering of a shadcn dialogue is bad practice? according to shadcn?

wise quest
#

if no argument is provided, it simply falls back to defaultValues, which you can update if needed

grim hazel
grim hazel