Dependency Yup Custom Validation trong ReactJS (Phần 1)

Yup Validation

Yup validation được sử dụng tương đối rộng rãi trong các form submitter của các app sử dụng JavaScript Frameworks mà cụ thể ở trong bài viết này là ReactJS. Về cơ bản, nó được thiết kế để cho front-end browser có thể xác thực dữ liệu được nhập vào trước khi truyền đến server để tránh các lỗi không mong muốn xảy ra với việc render component hoặc tệ hơn là crash app. Yup hỗ trợ rất nhiều các default validation, bạn có thể dễ dàng tham khảo ở đây : https://github.com/jquense/yup Trong bài viết này, mình sẽ tập trung chủ yếu về cách làm sao để viết các dependency custom validations-validate 1 hoặc nhiều fields phụ thuộc vào giá trị của một field khác.

Hoàn cảnh sử dụng

<SelectField name="selectField" value={values.selectField} />
{
    values.selectField === "Yes" 
        && (
        <DependentSelectField name="dependentSelectField" value={values.dependentSelectField} />
        <InputField name="inputField" value={values.inputField} />
        ) 
}

Trong trường hợp trên, 2 fields "Dependent Select Field" và "Input" chỉ xuất hiện và bắt đầu được validate khi "Select Field" có giá trị là "Yes" Trường hợp "Select Field" có giá trị là "No" thì 2 fields ở dưới sẽ không xuất hiện và thực hiện validate.

Cách sử dụng

Giả sử chúng ta có một Validation Schema của ví dụ trên là ExampleValidation, ở trong container, ta sẽ khai báo như bình thường:

withFormik({
    validationSchema: ExampleValidation

Trong file ExampleValidation.js, chúng ta sẽ có:

import * as Yup from 'yup';

export const exampleValidation = Yup.object().shape({
  selectField: Yup.string()
    .required('Required Field'), //default yup validation
  dependentSelectField: Yup.string()
    .requiredWith(Yup.ref('selectField'), 'Yes', 'Require when SelectField has Yes value'),
  inputField: Yup.string()
    .requiredWith(Yup.ref('selectField'), 'Yes, 'Require when SelectField has Yes value'),
});

Ở trên requireWith() là một method mới mà ta add vào, để tham chiếu value hiện tại của selectField lấy từ formik ra, dựa vào đó, 2 fields: dependentSelectField và inputField sẽ bắt validate khi value của selectField là 'Yes'.

Cụ thể, requireWith():

function requiredWith(ref, expectedValue, msg) {
  return this.test({
    name: 'requiredWith',
    exclusive: false,
    message: msg,
    test(value) { return (this.resolve(ref) !== expectedValue) || (this.resolve(ref) === expectedValue && !!value); }, //value là giá trị hiện tại của field gọi method
  });
}

Yup.addMethod(Yup.mixed, 'requiredWith', requiredWith);

Value của SelectField sẽ được đọc thông qua method: this.resolve(ref). Ở trên chúng ta sẽ xác nhận, nếu như giá trị của selectField !== expectedValue là 'Yes' hoặc value của selectField === 'Yes' và value của field hiện tại không null (có giá trị) thì sẽ pass validation, còn nếu không thỏa mãn điều kiện đó thì nó sẽ trả lại lỗi với msg được truyền vào là: 'Require when SelectField has Yes value'.

Tạm kết

Trên đây là một validation đơn giản để check requirement của một/nhiều fields phụ thuộc vào value của field khác với điều kiện có cùng level. Nhưng với trường hợp, ta cần phải lấy giá trị của 1 field với level cao hơn, như là 1 field được khái báo trong object tạm đặt là ObjectValidation, cần lấy selectValue thì như thế nào? Ta không thể gọi Yup.ref('selectField') được vì lúc này, selectValue sẽ được tự động hiểu là ObjectValidation.selectValue (không tồn tại). Trong trường hợp này, mình sẽ làm rõ ở phần 2 của bài viết.