iway1 / react-ts-form

https://react-ts-form.com
MIT License
2.01k stars 35 forks source link

Is there a way to conditionally show/hide fields depending on value of other fields in the form #34

Open daksh-sagar opened 1 year ago

daksh-sagar commented 1 year ago

let's say i have a checkbox field - billing address is different from shipping address. I want to show additional fields when this checkbox is ticked (with their own set of validations), and hide these fields when the checkbox is not selected.

iway1 commented 1 year ago

Right now there's not a simple way to do this with the schema-to-form approach - for these types of situations I think the easiest approach is to use plain JSX right now and build input components that work both with and without react-ts-form.

I do want to solve for this more elegantly as it's a common situation, but I'm not sure what the best solution is quite yet or if a good solution exists. Open to ideas 😃

I think implementing #21 might solve it to some extent? Will need to try it out and see if the DX is good or not.

Ultimately the solution may just be "build components that work both with and without react-ts-form, and if you need to hide certain fields then just use plain JSX with react hook form / zod".

Definitely could use some improved documentation with respect to this as well

MathisBarre commented 1 year ago

Not stupid but does useTsController work outside the context of react-ts-form ? If no, it seems complicate to create the same components for both case. Or maybe I'm missing something.

akd-io commented 1 year ago

It would be super cool to see a smooth DX for this case. It is the most annoying part of farms in my opinion, and when I found out about react-ts-form, conditional rendering was the first thing I looked for. Might be a hard nut to crack. I for one don't know how one would go about doing it.

Will keep eyes on this thread and let you know if I come up with anything.

iway1 commented 1 year ago

@akd-io yeah I'm pretty short on ideas for it honestly other than implementing dependent fields, might be the best we can do with schema based form generation? would love an elegant solution

iway1 commented 1 year ago

@MathisBarre currently, useTsController does not work outside of react-ts-form components. It could be possible to make it work outside of them, but IDK if it's a good idea.

With the prop forwarding feature, it's possible to build components that work both with and without react-ts-form:

import {useController} from 'react-hook-form'
function Component({control, name}:{control: Control, name: string}) {
  const {field: {onChange, value}} = useController({control, name});
}

Then to use it outside of react-ts-form but it would also be compatible with react-ts-form because control and name are automatically passed to any components that are rendered via a Form component generated by createTsForm.

The whole idea with prop forwarding was too support these situations - but totally agree building a component for each use case would be bad

rvetere commented 1 year ago

I would recommend you to provide a good example in the docs - because this is one main reason why we drop this library now in the evaluation of my company... maybe with good docs, how to write a component to re-use it in both context could help :)

I cannot even access the underlaying "watch" function of react-hook-form to access the state of a single field, just to begin a toggle... I could think of a solution where we push states of a specific field into a context, and then create some complex schemas for the cases where toggling is involved.. then we could access this state from the context and display/hide fields in the complex schema... would this be a solution? Could work in my eyes, but the problem starts that no "watch" is accessible with ts-form... if i need to implement such forms with react-hook-form - then i drop ts-form and zod completely, what saves me about 45kb of bundled code 🤷 This really feels bad as everything else would be AWESOME regarding the DX..

iway1 commented 1 year ago

@rvetere watch should be usable, you'll just need to pass in the form prop to the ts-form component:

const form = useForm({resolver: zodResolver(Schema)});
const watch = useWatch({control: form.control});

return <MyForm form={form}/>

Feel free to open an issue if that's causing any issues though.

scamden commented 1 year ago

FYI conditional show / hide of fields definitely works now with the custom layouts feature of CustomChildComponent added in https://github.com/iway1/react-ts-form/pull/66

weirdly useForm didn't work properly for me inside this child (@iway1 thoughts on why? edit: this is because i should RTFM and use useFormContext instead) but useWatch works a treat and is more elegant anyway.

const schema = z.object({
    startDate: z.date(),
    endDate: z.date().nullish(),
    showEndDate: z.boolean(),
  });
  function CustomChildComponent({
    startDate,
    showEndDate,
    endDate,
    ...rest
  }: RenderedFieldMap<typeof schema>) {
    const showEndDateValue = useWatch({ name: 'showEndDate' });
    return (
      <>
        {startDate}
        {showEndDateValue && endDate}
        {showEndDate}
      </>
    );
  }

    <ZodForm schema={schema} onSubmit={() => {}}>
      {CustomChildComponent}
    </ZodForm>