iway1 / react-ts-form

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

Allow async .superRefine #70

Closed christopher-kapic closed 1 year ago

christopher-kapic commented 1 year ago

Is your feature request related to a problem? Please describe. I want to use asynchronous functions to validate my schemas (using .superRefine). Specifically, I want to check my backend to make sure that the username and email aren't already in use, like so:

const CreateUserSchema = z
  .object({
    username: z.string().describe("Username // username"), // renders TextField
    email: z
      .string()
      .email("Enter a real email please.")
      .describe("Email // johndoe@example.com")
      .optional(), // renders TextField
    name: z.string().describe("Name // John Doe").optional(),
  })
  .superRefine(async (values, ctx) => {
    const zodEmail = z.string().email().safeParse(values.email);
    const [userByUsername, userByEmail] = await getUsernameAndEmail(values.username, zodEmail.data);
    if (userByUsername.user !== null) {
      ctx.addIssue({
        code: "custom",
        message: "Username already taken.",
        path: ["username"],
      });
    }
    if (userByEmail.user !== null) {
      ctx.addIssue({
        code: "custom",
        message: "Email already taken.",
        path: ["email"],
      });
    }
  });

Technically this works, but I have to adjust my .eslint.json to include "@typescript-eslint/no-misused-promises": "warn", which is undesirable.

Describe the solution you'd like Initially I thought this was a bug in Zod. I opened an issue, and got a response that I might need to be using parseAsync. I think switching to that might be the solution (although I am admittedly not super familiar with the react-ts-form source code, so take that with a grain of salt).

Describe alternatives you've considered For now, I'm just leaving my "@typescript-eslint/no-misused-promises": "warn" as is. Not ideal, but not progress-stopping either.

Additional context Add any other context or screenshots about the feature request here.

iway1 commented 1 year ago

Is there something library isn't doing that you want it to do? this seems like a linting issue with zod itself?

christopher-kapic commented 1 year ago

I opened an issue with Zod asking them to allow async methods and got the response that I should use parseAsync.

iway1 commented 1 year ago

ah okay well we don't even call parse or parseAsync in the library at the moment, react-hook-form's zod resolver is what calls it. Not sure if we can implement this since we're relying on RHF for this part? they might be able to fix it in the zod resolver package which would fix it for this lib

christopher-kapic commented 1 year ago

I think we could pass an option to the zodResolver since react-hook-form checks for async here. I can work on this later today.

iway1 commented 1 year ago

@christopher-kapic oh nice didn't know that, that shouldn't be to bad to add in.

I guess it's currently indirectly supported if you pass in a form prop (since you can pass your own resolver there) - but might be nice to just have a zodResolverOptions prop or something on the Form component to make that customizable.

TBH I'm a little wary of this with how the zod resolver is working currently in the library, it's getting called twice because of some workaround I had to implement to get react-hook-form to work nicely. So the parse would get called twice with the current code which isn't a big deal normally but when there's an API request involved it's probably a bigger issue...

Really want to get that fixed but IDK if it's possible as long as we're using RHF

iway1 commented 1 year ago

Going to close for now because there are no current plans to support this. I think long term we want to get off of RHF to reduce bundle size which would allow doing super refine.

To achieve the same behavior in with this library, you'll need to do server validation outside of zod and react-ts-form.