21

I'm trying to use Yup along with Formik in my react form. The form fields are going to be dynamic so as their validations.

export const formData = [
  {
    id: "name",
    label: "Full name",
    placeholder: "Enter full name",
    type: "text",
    required: true,
    value: "User name",
    values: [],
    validations: [
      {
        type: "minLength",
        value: "5",
        error_message: "name should be atleast 5 char long"
      },
      {
        type: "maxLength",
        value: "10",
        error_message: "name should be atleast 5 char long"
      }
    ]
  },
  {
    id: "email",
    label: "Email",
    placeholder: "Email",
    type: "text",
    required: true,
    value: "email",
    values: [],
    validations: [
      {
        type: "minLength",
        value: "5",
        error_message: "name should be atleast 5 char long"
      },
      {
        type: "maxLength",
        value: "10",
        error_message: "name should be atleast 5 char long"
      },
      {
        type: "email",
        error_message: "Valid email"
      }
    ]
  },
  {
    id: "phoneNumber",
    label: "phone number",
    type: "text",
    required: true,
    value: "7878787878",
    values: [],
    validations: [
      {
        type: "minLength",
        value: "5",
        error_message: "name should be atleast 5 char long"
      },
      {
        type: "maxLength",
        value: "10",
        error_message: "name should be atleast 5 char long"
      },
      {
        type: "required",
        error_message: "phone number is required"
      }
    ]
  },
  {
    id: "total",
    label: "Total People in Family",
    placeholder: "family members count",
    type: "text",
    required: false,
    value: "1",
    values: [],
    validations: [
      {
        type: "minLength",
        value: "1",
        error_message: "there should be atleast 1 family member"
      },
      {
        type: "maxLength",
        value: "5",
        error_message: "max family members can be 5"
      }
    ]
  }
]

 let validateSchema = yup.object().shape({
     name: yup.string().required("name is required"),
     email: yup.string().email(),
     phoneNumber: yup.number().min(10, "minium 10 numbers"),
     total: yup
       .number()
       .min(1, "minium 1 member")
       .max(5, "max 5 member")
       .required("member is required")    });
  • What I'm currently doing is iterating over the above array and calling the corresponding React form components.
  • Validation is currently handled by Yup. I'm aware that you can create static Yup validation schema like above `validateSchema' variable.
  • Now I want to create this validation schema depending upon the values in the formData.validation array. I tried some of the ways in this codesandbox but still unable to figure it out. Also, I looked into the Yup.lazy but it seems highly confusing to me.

Any help will be appreciated :)

Codesandbox

vijayscode
  • 1,905
  • 4
  • 21
  • 37

2 Answers2

31

In case someone is trying to create yupschema on the fly. With some help, I was able to do it.

import * as yup from "yup";

export function createYupSchema(schema, config) {
  const { id, validationType, validations = [] } = config;
  if (!yup[validationType]) {
    return schema;
  }
  let validator = yup[validationType]();
  validations.forEach(validation => {
    const { params, type } = validation;
    if (!validator[type]) {
      return;
    }
    console.log(type, params);
    validator = validator[type](...params);
  });
  schema[id] = validator;
  return schema;
}

Codesandbox

vijayscode
  • 1,905
  • 4
  • 21
  • 37
2

If you're looking for more functionality, consider schema-to-yup.

But @vijayscode's answer is super handy. Here's an attempt to extend their example to include when conditions:

import * as yup from "yup";

function createYupSchema(schema, config) {
  const { id, validationType, validations = [] } = config;
  if (!yup[validationType]) {
    return schema;
  }
  let validator = yup[validationType]();
  validations.forEach((validation) => {
    const { params, type } = validation;
    if (!validator[type]) {
      return;
    }
    if (type === "when") {
      const { is, then, otherwise } = params[1];
      let whenParams = {};
      whenParams.is = is;
      whenParams.then = (schema) => schema[then[0].type](...then[0].params);

      if (otherwise) {
        whenParams.otherwise = (schema) =>
          schema[otherwise[0].type](...otherwise[0].params);
      }

      validator = validator["when"](params[0], whenParams);
    } else {
      validator = validator[type](...params);
    }
  });
  schema[id] = validator;
  return schema;
}

And define your config like so:

const myConfig = [
  {
    id: "isBig",
    validationType: "boolean",
  },
  {
    id: "count",
    validationType: "number",
    validations: [
      {
        type: "when",
        params: [
          "isBig",
          {
            is: true,
            then: [
              {
                type: "min",
                params: [5],
              },
            ],
            otherwise: [
              {
                type: "min",
                params: [0],
              },
            ],
          },
        ],
      },
    ],
  },
];

const schema = myConfig.reduce(createYupSchema, {});
const validateSchema = yup.object().shape(schema);
spatialaustin
  • 582
  • 4
  • 21
  • Hey I have spent some time trying to make yup-to-schema work for more advanced scenario and I must strongly advise against it. It does support only basic stuff and is convoluted beyond imagination. Keep it simple – Dominik Jan 17 '23 at 17:13