Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Yup when condition inside nested object

so i have a problem with conditional validation using yup. Basically i want shipping to have required properties when the checkbox is not toggled. I am using yup's when feature and i can't figure out how to reference the sameShippingAsBilling boolean value from that shipping nested object.

When checkbox is false, everything is working fine, errors are shown... But when checkbox is checked, i get this error: "Cannot use 'in' operator to search for 'default' in undefined"

The problem is that the value in is callback is undefined. Is it even possible to access the outer variables in when from those nested objects or not. What is the alternative way to do this?

Here is the sandbox example

const schema = Yup.object({
  sameShippingAsBilling: Yup.bool(),
  billing: Yup.object({
    line1: Yup.string().required(),
    city: Yup.string().required(),
    country: Yup.string().required()
  }),
  shipping: Yup.object({
    line1: Yup.string().when("sameShippingAsBilling", {
      is: !val || val === false,
      then: Yup.string().required(),
    }),
    city: Yup.string().when("sameShippingAsBilling", {
      is: !val || val === false,
      then: Yup.string().required(),
    }),
    country: Yup.string().when("sameShippingAsBilling", {
      is: !val || val === false,
      then: Yup.string().required(),
    })
  })
});

const addrValues = { line1: "", city: "", country: "" };

const formOpts = {
  mode: "onChange",
  reValidateMode: "onChange",
  defaultValues: {
    sameShippingAsBilling: true,
    billing: { ...addrValues },
    shipping: { ...addrValues }
  },
  resolver: yupResolver(schema)
};

export default function CheckoutForm() {
  const methods = useForm(formOpts);
  const { watch } = methods;

  const shippingAsBilling = watch("sameShippingAsBilling");

  const onSubmit = async (data) => {
    console.log(data);
  };

  return (
    <MuiThemeProvider theme={createMuiTheme()}>
      <FormProvider {...methods}>
        <form onSubmit={methods.handleSubmit(onSubmit)} noValidate>
          <pre>errors: {JSON.stringify(methods.errors, null, 2)}</pre>

          <div className="group">
            <FormTextField name="billing.line1" />
            <FormTextField name="billing.city" />
            <FormTextField name="billing.country" />
          </div>

          <div>
            <FormCheckbox name="sameShippingAsBilling" />
          </div>

          {!shippingAsBilling && (
            <div className="group">
              <FormTextField name="shipping.line1" />
              <FormTextField name="shipping.city" />
              <FormTextField name="shipping.country" />
            </div>
          )}

          <div>
            <button type="submit">Pay</button>
          </div>
        </form>
      </FormProvider>
    </MuiThemeProvider>
  );
}
like image 388
dankobgd Avatar asked Mar 08 '26 07:03

dankobgd


1 Answers

You can conditionally render the shape of your shipping object dependent on the sameShippingAsBilling value.

Something along these lines:

    // define the boolean that stores if shipping details are the same as billing
    sameShippingAsBilling: Yup.bool(),
    shipping: Yup.object().when("sameShippingAsBilling", {
        // check if the boolean is false,   
        is: (sameShippingAsBilling) => sameShippingAsBilling === false,
        // if it is false, then use an object with required properties
        then: Yup.object().shape({
          line1: Yup.string().required(),
          city: Yup.string().required(),
          country: Yup.string().required(),
        }),
        // otherwise use an object without required properties
        otherwise: Yup.object().shape({
          line1: Yup.string(),
          city: Yup.string(),
          country: Yup.string(),
        }),
      }),
like image 114
jolo Avatar answered Mar 09 '26 19:03

jolo



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!