Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

zod's superRefine not working properly #538

Open
rlesniak opened this issue Apr 5, 2023 · 31 comments
Open

zod's superRefine not working properly #538

rlesniak opened this issue Apr 5, 2023 · 31 comments
Assignees
Labels
question Further information is requested

Comments

@rlesniak
Copy link

rlesniak commented Apr 5, 2023

Describe the bug
I want to use zod's superRefine validation function. hook form shows that form is invalid, but errors object is empty

To Reproduce
Steps to reproduce the behavior:

  1. Go to https://stackblitz.com/edit/vitejs-vite-dwvjn3?file=src/App.jsx
  2. See that isValid is false but errors object is empty

Codesandbox link (Required)
https://stackblitz.com/edit/vitejs-vite-dwvjn3?file=src/App.jsx

Expected behavior
error object should have Block is too short error message

Desktop (please complete the following information):

  • OS: macOS Ventura 13
  • Browser Chrome
  • Version 111
@jorisre
Copy link
Member

jorisre commented Apr 6, 2023

I doubt that the problem is linked to Zod's superRefine since the same issue arises with other resolvers like Yup, as I have tested. Although formState.isValid is true without a resolver, it is initially false when used with a resolver.

@bluebill1049 , could it be possible that there is something related to the usage of resolvers on the react-hook-form's end?

@rlesniak
Copy link
Author

rlesniak commented Apr 6, 2023

Its for sure not zods fault because its error object, either for common validation like nonempty() or superRefine is consistent
image

@bluebill1049
Copy link
Member

bluebill1049 commented Apr 11, 2023

Have you tried debug with the following:

resolver: async (data, context, options) => {
  // you can debug your validation schema here
  console.log('formData', data)
  console.log('validation result', await anyResolver(schema)(data, context, options))
  return anyResolver(schema)(data, context, options)
},

@bluebill1049 bluebill1049 added the question Further information is requested label Apr 11, 2023
@jorisre
Copy link
Member

jorisre commented Apr 12, 2023

@bluebill1049 I did it as I said in my previous reply

@GuhPires
Copy link

Having the same issue when using the refine method as well. Debugged as @bluebill1049 had shown and the output is exactly as expected by the resolver: an object with values and errors (with the error "thrown" by the refine function populated in that object).

The isValid prop is updated accordingly but the errors object is still empty

@ms10596
Copy link

ms10596 commented Jun 20, 2023

I'm facing the same issue using superRefine.

@emielvanseveren
Copy link

Having the same issue as @GuhPires.

@nag-murali
Copy link

Facing the same issues with superRefine

@jorisre
Copy link
Member

jorisre commented Aug 17, 2023

@bluebill1049 Any thoughts?

@bluebill1049
Copy link
Member

@bluebill1049 Any thoughts?

I will take a look at it 👍

@bluebill1049 bluebill1049 self-assigned this Aug 17, 2023
@nag-murali
Copy link

Describe the bug
using zod's superRefine validation function. errors object is empty.
Its working fine in one order not the other way. ( check this video)
Link: (Screen Recording 2023-08-21 at 2.49.16 PM.zip)
Error Video Link: (https://github.com/react-hook-form/resolvers/assets/93373775/82cebe8b-228b-4a7b-a4fd-980cf97310bb)

To Reproduce
Steps to reproduce the behavior:

Go to https://codesandbox.io/p/sandbox/elated-mendeleev-v92vvs
See that errors object is empty, even though validation is correct.

Expected behavior
errors object of the react form should have the errors of the respective fields.

Desktop (please complete the following information):

OS: macOS Ventura 13
Browser Chrome
Version 116

Please let me know if the way of implementation is wrong !!

@stramel
Copy link
Contributor

stramel commented Aug 22, 2023

@bluebill1049 I noticed when trying to use the .refine and .superRefine it would work depending on placement. This was due to the fact that the ref wouldn't necessarily be populated if there wasn't something registered for the path I was validating.

It would be nice if it could follow how setError works, and allow paths to register errors

@bluebill1049
Copy link
Member

hey @stramel 👋 I think the whole scheme validation is based on registered input, not sure if it's a straightforward implementation switch and setError was a single path update which is a lot easier.

@stramel
Copy link
Contributor

stramel commented Aug 22, 2023

Hey @bluebill1049 👋🏼

I'm currently using the Zod schema validation pretty easily but there are a couple of cases that aren't working well for me.

I have:

  • .refine/.superRefine for a non-registered input but for an array of objects, validating something on the object. These objects have a property that is registered.
  • validating the schema, the errors aren't being cleared after applying to a registered input (probably a separate issue)

The setError was more referring to how it can apply an error to a non-registered input.

@bluebill1049
Copy link
Member

validating the schema, the errors aren't being cleared after applying to a registered input (probably a separate issue)

A separate issue, hook form is field level based for validation and consistent validation strategy. I don't have a solution in my head without re-render the entre form on each form state.

@stramel
Copy link
Contributor

stramel commented Aug 28, 2023

validating the schema, the errors aren't being cleared after applying to a registered input (probably a separate issue)

A separate issue, hook form is field level based for validation and consistent validation strategy. I don't have a solution in my head without re-render the entre form on each form state.

That's fair, I will come up with a workaround for that.

I will note though, that the isValid property still shows invalid even though there are no errors since Zod reports errors but they don't exist within the fields. I would expect that the errors object would have something if the isValid property is reporting invalid.

@camin-mccluskey
Copy link

Also facing this issue isValid is false with empty errors object. Does anyone know if manually setting the error path in refine results in an error object? (I don't think this would be a solution in my case however, as the path is dynamic as part of an array input).

@bu3alwa
Copy link

bu3alwa commented Sep 24, 2023

This only happens on first load but if you have a form and you submit or have it re-validate on change it will give you the error object.

@simpros
Copy link

simpros commented Nov 29, 2023

Any update on this?

1 similar comment
@douglasgomes98
Copy link

Any update on this?

@FoiDot
Copy link

FoiDot commented Mar 11, 2024

Hi! Any update on this issue? I'm manually triggering the validation if isValid = false and all the inputs are filled. But it will be nice to have a native solution.

@rinqtmith
Copy link

I had same issue and I have tried adding proper path to addIssue and now it works. Before adding path, error was there without a path.

  .superRefine((data, ctx) => {
    if (data.table === "linear" && data.dimension < 0.5) {
      ctx.addIssue({
        code: z.ZodIssueCode.too_small,
        minimum: 0.5,
        type: "string",
        inclusive: true,
        message: "Value cannot be smaller than 0.5.",
        path: ["dimension"],
      });
    }}

@stefan-girlich
Copy link

stefan-girlich commented Mar 22, 2024

Hi! Any update on this issue? I'm manually triggering the validation if isValid = false and all the inputs are filled. But it will be nice to have a native solution.

@FoiDot Would you mind sharing the code for your solution? I tried the following, but errors is still empty:

useEffect(() => {
    if (!isValid) trigger('batches')
  }, [isValid])

@FoiDot
Copy link

FoiDot commented Mar 22, 2024

Hi! Any update on this issue? I'm manually triggering the validation if isValid = false and all the inputs are filled. But it will be nice to have a native solution.

@FoiDot Would you mind sharing the code for your solution? I tried the following, but errors is still empty:

useEffect(() => {
    if (!isValid) trigger('batches')
  }, [isValid])

@stefan-girlich I did something like this:

useEffect(() => {
        // I have some inputs working with Controller and setState.
        // That means mode = 'all' | 'onChange' | etc doesn't work
        // So I need to clean the errors manually with the clearErrors from useForm.
        if (isValid) clearErrors()
       // Check if all your inputs are filled
        else if (
            !isValid &&
            watch('bank') &&
            watch('accountType') &&
            watch('accountNumber')
        )
            trigger()
 }, [isValid])

Make sure to have your path in your schema

if (accountNumber.startsWith('10') && bank === '01')
       ctx.addIssue({
       code: z.ZodIssueCode.custom,
       path: ['accountNumber'],
       message: `Your Account number cannot start with 10`,
       fatal: true,
 })

return z.NEVER

@MusaGillani
Copy link

following

@stefan-girlich
Copy link

stefan-girlich commented Mar 25, 2024

This fix worked for me.
tldr:

- path: [...ctx.path, idx, 'myProperty'],
+ path: [`[${idx}].myProperty`],

Kudos @bluebill1049 for the debug code; I wouldn't have found this issue without it. 💯

@GPetrites
Copy link

I'm running into the same issue. In my case the superRefine is needing to flag an error on a field which has not been edited. This field is only required if another field has a given value. When this happens, RHF is recognizing that the form is invalid but doesn't add the error to the field or form.

I can get around this, mostly, by using the trigger function on the form to tell RHF to validate the fields that were never edited.

You can see this in the Codesandbox link in the original error report. When you run the sandbox as-is, the form is invalid with no errors. But if you add the following at line 24 then the error message appears on the form:

form.trigger("start")

This is not quite optimal as I need to remember to call trigger to force RHF to fully recognize the validation error.

Bottom line, if superRefine is flagging an error on a field which is not dirty (not touched?), somehow add a call to trigger tied to an event on the form which tells the form to check if the field is valid. For example:

    useEffect(() => {
        trigger(["fieldReceivingError"]);
    }, [fieldCausingError.field.value]);

@andredewaard
Copy link

andredewaard commented Jul 18, 2024

Same issue here, I have an object where you need to fill in a start and end time, i have a refine on the object that checks if the start_time is before the end_time, i can throw the error on the start_time like this.

export const CreateWorkActivityRequest = object({
  start_time: date(),
  end_time: date(),
}).refine(
  (val) => {
    if (val.type !== 'travel') {
      return !isAfter(val.start_time, val.end_time)
    }
    return true
  },
  {
    message: 'Start tijd is later dan eind tijd',
    path: ['start_time']
  }
)

But i want to make use of the 'root' property in the errors so i can show the error at the end of the form like we do with server errors

setError("root.serverError", {
  type: "400",
})

but throwing this error will make the form pass but the object its sending is empty

.refine(
  (val) => {
    if (val.type !== 'travel') {
      return !isAfter(val.start_time, val.end_time)
    }
    return true
  },
  {
    message: 'Start tijd is later dan eind tijd',
    path: ['root', 'start_end_time_mismatch']
  }
)

with superRefine this also works

.superRefine(
  (val, ctx) => {
    if (val.type !== 'travel' && isAfter(val.start_time, val.end_time)) {
      console.log('validation failed')
      ctx.addIssue({
        code: ZodIssueCode.invalid_date,
        message: 'Start tijd is later dan eind tijd',
        path: ['start_time'],
      });
      return NEVER
    }
  },
)

but this doenst

.superRefine(
  (val, ctx) => {
    if (val.type !== 'travel' && isAfter(val.start_time, val.end_time)) {
      console.log('validation failed')
      ctx.addIssue({
        code: ZodIssueCode.invalid_date,
        message: 'Start tijd is later dan eind tijd',
        path: ['root', 'start_end_time_mismatch'],
      });
      return NEVER
    }
  },
)

@ilbertt
Copy link

ilbertt commented Oct 15, 2024

See that isValid is false but errors object is empty

I'm having the same error

@drochag
Copy link

drochag commented Jan 19, 2025

This is still happening 😭

@eugeneford
Copy link

Same problem. Any resolution?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
question Further information is requested
Projects
None yet
Development

No branches or pull requests