zod icon indicating copy to clipboard operation
zod copied to clipboard

Zod/ React Hook Form - Dynamic Field Array - Conditional Field

Open danielbattat opened this issue 1 year ago • 4 comments

I am trying to create a validation scheme for conditional fields within a field array.

For example, the data can include a list of persons, where first and last name are always required, but address is only required if a box is checked off indicating 'different from above'.

Valid data can look like this:

[
  {
      'firstName': 'John', //required string
      'lastName': 'Smith', //required string
      'addressSameAsAbove': false, //required bool,
      'address': //required string
 },
 {
      'firstName': 'John', //required string
      'lastName': 'Smith', //required string
      'addressSameAsAbove': true, //required bool,
      //address not required
 }

I was able to accomplish this using refine, however, since the form structure is created dynamically, I need to be able to create the schema dynamically as well based on a form schema like this:

[
  {
    'type': 'Array',
    'children': [
       { type: 'text', name: 'firstName', required: true },
       { type: 'text', name: 'lastName', required: true },
       { type: 'bool', name: 'addressSameAsAbove', required: true },
       { type: 'text', name: 'address', required: { field: 'addressSameAsAbove', condition: false } },
   ]
]

I am able to create the schema dynamically for all fields by creating an object for each 'type' of field, however, I am unable to create the conditional validation based on another field.

Any direction / insight would be appreciated. Is this possible using zod?

danielbattat avatar Jul 07 '24 22:07 danielbattat

I had a similar challenge. The solution I came up with was to dynamically create a new schema that is passed into the react-hook-form zodResolver function based on the current fields in the form.

I start with a master schema object that has all possible fields in the form, their related validations, and a bunch of other stuff. Then, when the user does something that causes the shape of the form to change, the app calls my custom getValidationSchema function with the current list of fields as the argument. getValidationSchema builds and returns the new schema which is then passed back into zodResolver.

Hope this helps.

generalleger avatar Jul 11 '24 22:07 generalleger

I had a similar challenge. The solution I came up with was to dynamically create a new schema that is passed into the react-hook-form zodResolver function based on the current fields in the form.

I start with a master schema object that has all possible fields in the form, their related validations, and a bunch of other stuff. Then, when the user does something that causes the shape of the form to change, the app calls my custom getValidationSchema function with the current list of fields as the argument. getValidationSchema builds and returns the new schema which is then passed back into zodResolver.

Hope this helps.

Thank you! How do "refresh" RHF with the new schema? There is no method to update the resolver

danielbattat avatar Jul 14 '24 19:07 danielbattat

I had a similar challenge. The solution I came up with was to dynamically create a new schema that is passed into the react-hook-form zodResolver function based on the current fields in the form. I start with a master schema object that has all possible fields in the form, their related validations, and a bunch of other stuff. Then, when the user does something that causes the shape of the form to change, the app calls my custom getValidationSchema function with the current list of fields as the argument. getValidationSchema builds and returns the new schema which is then passed back into zodResolver. Hope this helps.

Thank you! How do "refresh" RHF with the new schema? There is no method to update the resolver

Correct. You have to force the component to rerender. In my case, I had to rebuild the entire form when the schema changed, so that was forcing the component to rerender.

I'm not sure about the particulars of your situation, but the easiest way is probably to add a call to RHF's watch() method in the component. That's probably not the most elegant solution, since it would create a rerender on every keystroke into the form. But I am sure you can find a way to suppress unnecessary rerenders if it actually impacts the user experience.

generalleger avatar Jul 14 '24 19:07 generalleger

I had a similar challenge. The solution I came up with was to dynamically create a new schema that is passed into the react-hook-form zodResolver function based on the current fields in the form. I start with a master schema object that has all possible fields in the form, their related validations, and a bunch of other stuff. Then, when the user does something that causes the shape of the form to change, the app calls my custom getValidationSchema function with the current list of fields as the argument. getValidationSchema builds and returns the new schema which is then passed back into zodResolver. Hope this helps.

Thank you! How do "refresh" RHF with the new schema? There is no method to update the resolver

I used trigger();, u can await it.

3pleFly avatar Oct 01 '24 18:10 3pleFly

Hi @danielbattat! I'm Dosu and I’m helping the zod team.

You're right—React Hook Form doesn't provide a built-in way to update the resolver after the form is initialized. The most common workaround is to remount the form by changing its key prop whenever your schema changes, which forces RHF to re-initialize with the new resolver.

Another approach is to use a dynamic schema function and manually trigger validation on affected fields when dependencies change. For example, you can use a useEffect with watch and trigger to re-validate fields that depend on others (like conditional requirements) see discussion:

useEffect(() => {
  const sub = watch((_, { name }) => {
    if (name === 'addressSameAsAbove') {
      trigger('address');
    }
  });
  return () => sub.unsubscribe();
}, [watch, trigger]);

If you need more targeted help, feel free to share a minimal reproduction. If this answers your question, please close the issue!

To reply, just mention @dosu.


How did I do? Good | Irrelevant | Incorrect | Verbose | Hallucination | Report 🐛 | Other  Join Discord Share on X

dosubot[bot] avatar Jul 21 '25 23:07 dosubot[bot]