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

check using enum does not validate #2

Closed
hyusetiawan opened this issue Sep 8, 2021 · 14 comments
Closed

check using enum does not validate #2

hyusetiawan opened this issue Sep 8, 2021 · 14 comments
Assignees
Labels
bug Something isn't working

Comments

@hyusetiawan
Copy link

given the following schema:

const NonOccupantType = z.object({
    product: z.enum(['Ho3', 'Dp3']),
    occupantType: z.null(),
});
const RequireOccupantType = z.object({
    product: z.literal('Ho6'),
    occupantType: z.nativeEnum(OCCUPANT),
});
const validationSchema = z.union([NonOccupantType, RequireOccupantType]);

validating using formik the following object passes (the onSubmit gets called):
{product: "Ho6", occupantType: ""}
and passing that same object using .parse() correctly throws on the enum value validation

@robertLichtnow robertLichtnow added the bug Something isn't working label Sep 10, 2021
@robertLichtnow robertLichtnow self-assigned this Sep 10, 2021
@robertLichtnow
Copy link
Owner

robertLichtnow commented Sep 10, 2021

@hyusetiawan Can you provide the OCCUPANT enum, so I can replicate?

EDIT: Also, please provide the zod and formik versions that you're using

@hyusetiawan
Copy link
Author

@robertLichtnow

export enum OCCUPANT {
    OWNER = 'owner',
    RENTER = 'renter',
}

the versions:
"formik": "^2.2.9",
"zod": "^3.8.2"

@robertLichtnow
Copy link
Owner

robertLichtnow commented Sep 10, 2021

@hyusetiawan I've made a small example here:

import { z } from 'zod';
import { Formik } from 'formik';
import { toFormikValidationSchema } from 'zod-formik-adapter';

export enum OCCUPANT {
  OWNER = 'owner',
  RENTER = 'renter',
}

const NonOccupantType = z.object({
  product: z.enum(['Ho3', 'Dp3']),
  occupantType: z.null(),
});
const RequireOccupantType = z.object({
  product: z.literal('Ho6'),
  occupantType: z.nativeEnum(OCCUPANT),
});

const validationSchema = z.union([NonOccupantType, RequireOccupantType]);

function ExampleForm() {
  return (
    <Formik
      validationSchema={toFormikValidationSchema(validationSchema)}
      initialValues={{
        product: "",
        occupantType: "",
      }}
      onSubmit={console.log}
      validateOnMount={true}
    >
      {({ handleSubmit, handleChange, isValid, handleBlur, errors, touched, values }) => (
        <form onSubmit={(e) => {
          e.preventDefault();
          handleSubmit(e);
        }}>
          <div>
            <span>product</span>
            <input
              value={values.product}
              onChange={handleChange("product")}
              onBlur={handleBlur("product")}
            />
            {
              touched.product &&
              errors.product &&
              <strong>Error: {errors.product}</strong>
            }
          </div>
          <div>
            <span>occupantType</span>
            <input
              value={values.occupantType}
              onChange={handleChange("occupantType")}
              onBlur={handleBlur("occupantType")}
            />
            {
              touched.occupantType &&
              errors.occupantType &&
              <strong>Error: {errors.occupantType}</strong>
            }
          </div>
          <button disabled={!isValid} type="submit">Submit</button>
        </form>
      )}
    </Formik>
  );
}

Debugging, I've found out that an error from a plain ZodSchema has this shape

[
  {
    "code": "invalid_enum_value",
    "options": [
      "owner",
      "renter"
    ],
    "path": [
      "occupantType"
    ],
    "message": "Invalid enum value. Expected 'owner' | 'renter', received ''"
  }
]

But a union, has a different shape of error

[
  {
    "code": "invalid_union",
    "unionErrors": [
      {
        "issues": [
          {
            "code": "invalid_enum_value",
            "options": [
              "Ho3",
              "Dp3"
            ],
            "path": [
              "product"
            ],
            "message": "Invalid enum value. Expected 'Ho3' | 'Dp3', received 'Ho6'"
          },
          {
            "code": "invalid_type",
            "expected": "null",
            "received": "string",
            "path": [
              "occupantType"
            ],
            "message": "Expected null, received string"
          }
        ],
        "name": "ZodError"
      },
      {
        "issues": [
          {
            "code": "invalid_enum_value",
            "options": [
              "owner",
              "renter"
            ],
            "path": [
              "occupantType"
            ],
            "message": "Invalid enum value. Expected 'owner' | 'renter', received ''"
          }
        ],
        "name": "ZodError"
      }
    ],
    "path": [],
    "message": "Invalid input"
  }
]

This shape is not mapped by this lib. I'll attempt to make a fix for this later on, but feel free to open a PR to comply with this fix. I'll add a message to the README regarding this, so people are aware to not use zod unions for a while.

EDIT: The main problem are the multiple path peroperties inside each error, which I don't know how to map to the formik error object

@amatiasq-heydoc
Copy link

amatiasq-heydoc commented May 4, 2022

Managed to make this work by using validate instead of validationSchema. Any reason why validationSchema is preferred?

https://codesandbox.io/s/headless-night-tlyz19?file=/src/App.tsx

@awhitty
Copy link

awhitty commented May 12, 2022

@amatiasq-heydoc Thanks so much for the PR! I hope it's accepted soon.

Any reason why validationSchema is preferred?

I also wondered this -- I found one Formik form in a repo I maintain that uses both validate and validationSchema. The validate function implements logic essentially like "Your username cannot be the same as your password." I think the intention was that it isn't really a structural check that belongs in a schema but rather just business logic. I believe this could be handled just fine (and in a more simple manner, really) by tacking an additional .refine() onto the zod schema, and I see other cases in the repo where that's the norm.

I guess the takeaway is that maybe folks have additional validation outside of zod? It's not hard to compose that with zod, but I imagine an adapter function with usage like composeValidateFns(toFormikValidate(schema), validate) could help address that story.

@amatiasq-heydoc
Copy link

Interesting, we can extend toFormikValidate() to accept the original validate function as a second parameter, automatically composing the final function.

@robertLichtnow
Copy link
Owner

I can move forward the proposed PR, but wanted to know if the OP @hyusetiawan also believes that the proposed solution fixes the problem

@hyusetiawan
Copy link
Author

I have moved on from this project, unfortunately, but it looks like you are able to replicate on your end here:
#2 (comment)

@jaivinwylde
Copy link

any updates?

@Glazy
Copy link

Glazy commented Nov 7, 2022

I made formik-validator-zod for use in my own project and unions are working.

It builds on some of the solution proposed in #10 so only exposes a function for use with the validate prop; not validationSchema.

Feel free to give it a try and I hope it helps!

@jaivinwylde
Copy link

Appreciate it, it does help :)

@jarrodmedrano
Copy link

jarrodmedrano commented Nov 11, 2022

I can move forward the proposed PR, but wanted to know if the OP @hyusetiawan also believes that the proposed solution fixes the problem

robertLichtnow Robert, can you move forward with the PR?

@robertLichtnow
Copy link
Owner

Closing this since the proposed solution was merged and deployed at v1.2.0

@emilbonnek
Copy link

@robertLichtnow
Is this warning from the readme still relevant?

IMPORTANT: Currently, this library does not work with zod union. See more #2.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

8 participants