jaredpalmer / formik

Build forms in React, without the tears 😭
https://formik.org
Apache License 2.0
33.71k stars 2.77k forks source link

Set default Yup validation context to form values #1359

Open edwingustafson opened 5 years ago

edwingustafson commented 5 years ago

🚀 Feature request

Current Behavior

validateYupSchema makes the default context an empty object.

Desired Behavior

validateYupSchema sets the default context to the current values of the form.

Suggested Solution

Remove default parameter value {} for context, pass context || values as any to Yup validation.

Who does this impact? Who is this for?

Allows conditional Yup validation on fields to use other fields anywhere in the form via $foo notation.

Describe alternatives you've considered

506 describes how to override validate with a custom function that can invoke Yup validation with values or any other object. This proposal makes it the default without requiring special overrides everywhere needed, and without breaking any existing code already supplying a context parameter.

rally25rs commented 5 years ago

This would be extremely useful since context seems to be the only reasonable way to use the yup.when condition. Most common use case is a "set password" form where the password and confirm-password fields need to be equal.

Using .when seems to be the recommended approach from https://github.com/jquense/yup/issues/74 but this seems to not really be easy to do with Formik.

If either yup.when could check other values in the value (as opposed to the context) or there were a way to pass the Formik values as the context then it would make this common use care a lot simpler. 👍


Edit: After like 2 hrs of messing with yup I realized half my issue was that Formik passes undefined to yup when the actual value is '' (empty string), and also in yup if you don't use a $ prefix then it checks values instead of context.

I ended up being able to make a password + confirm password combo like this:

yup.object({
    password: yup.string(),
    password2: yup.string().when('password', {
      is: undefined,
      then: yup.string().notRequired(),
      otherwise: yup
        .string()
        .required()
        .oneOf([yup.ref('password')], 'passwords must match'),
    }),
  }),

Maybe this will help someone searching for a solution in the future :)

frontendtony commented 5 years ago

Thanks for the snippet @rally25rs, it was really helpful

clementoriol commented 5 years ago

I'll add my use case in favor of this feature request :

I sometimes have to do conditional validation based on my component state, and I'd love to keep the expressiveness of Yup to manage my form validation.

I could keep this state in form state to be able to use yup's mixed.when('key') but I think it makes more sense to be able to pass some of my component state to yup.validate context option (from yup's doc). That way I could reference this context in my validationSchema with mixed.when('$key') without keeping it in form state.

Being able to pass a schemaContext prop to Form or something like this would be great. Is this something you would consider?

LrVch commented 4 years ago

This example shows how to use context dynamically with formik https://codesandbox.io/s/oqvp75vxxz?from-embed

Joran182to commented 3 years ago

This example shows how to use context dynamically with formik https://codesandbox.io/s/oqvp75vxxz?from-embed

Provided example is not working in formik v2 due to braking change in validate (see https://github.com/formium/formik/issues/2167)

So for anyone who is trying to make it work in v2, you could do it by updating validate part as follows:

validate: (values, props) => {
    const context = props.context || {};

    const schema = yup.object({
      email: yup
        .string()
        .when(
          "$emailRequired",
          (isRequired, schema) => (isRequired ? schema.required() : schema)
        )
    });

    return schema
      .validate(values, { abortEarly: false, context })
      .then(() => ({}))
      .catch(err => {
        return yupToFormErrors(err);
      });
  },
pachkovska commented 2 years ago

Upvoting this feature request or rather up-commenting))). It would be really helpful to be able to pass context.