question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Add a function to validate form fields using Yup and custom async functions

See original GitHub issue

Is your feature request related to a problem? Please describe. Today it is not possible to to mix async validations with Yup validations. We can only pass an Yup object that will check the whole form or create our own functions if we want to validate the fields individually.

Describe the solution you’d like It would be great to have a generic function where we could pass the key of the field we want to validate with its value, the schema for that specific field and other optional parameters if the user need to pass them for the function.

Describe alternatives you’ve considered I made some tests for a project that I’m working and solved this problem creating a function to first solve the Yup validation and then the custom validations if they exist:

const validate = async (value, key, schema, params) => {
  return await schema.yupValidations[key]
    .validate(value)
    .then(value => {
      if (schema.customValidations[key])
        return schema.customValidations[key](value, params);
    })
    .catch(err => err.message);
};

export default validate;

For each form of the system I create a FormSchema file like the one below, where instead of having one Yup object for the whole form, I create one object for each field.

import * as Yup from "yup";
import api from "../services/api";

const SignupFormSchema = {
  email: Yup.string()
    .required("Email is required")
    .email("Invalid format"),
  name: Yup.string()
    .required("Name is required")
    .min(5, "Min length is 5"),
};

async function NameValidation(value) {
  try {
    const result = await api.get("https://swapi.co/api/people/1/?format=json");
    if (value !== result.data.name) return "Invalid name";
  } catch (err) {
    return "Could not validate name. Network error";
  }
}

export default {
  yupValidations: SignupFormSchema,
  customValidations: {
    name: NameValidation,
  }
};

When I register the component I pass the schemaValidate

inputRef={register({
        validate: value => schemaValidate(value, field, schema, params)
      })}

And the form could be written like this:

import React from "react";
import useForm from "react-hook-form";

import schema from "../../schemas/SignupFormSchema";

import TextInput from "../../components/TextInput";
import Button from "../../components/Button";

export default function SignupForm() {
  const { register, errors, handleSubmit, clearError } = useForm({
    mode: "onBlur"
  });

  const props = {
    register,
    errors,
    clearError,
    schema
  };

  const onSubmit = (data, e) => {
    e.preventDefault();
    console.log(data);
  };

  return (
    <form>
      <TextInput props={props} field="email" label="Email" />
      <TextInput props={props} field="name" label="Name" />
      <Button
        handleSubmit={handleSubmit(onSubmit)}
        label="submit"
        color="primary"
      />
    </form>
  );
}

Additional context You can check the example where I tested this solution with TextFields, Selects and DatePickers from Material-UI here: https://github.com/giovanniantonaccio/react-hook-form-test,

Issue Analytics

  • State:closed
  • Created 4 years ago
  • Reactions:14
  • Comments:6 (3 by maintainers)

github_iconTop GitHub Comments

15reactions
estshycommented, Nov 22, 2019

@giovanniantonaccio If I understood your issue correctly then I think it was facing similar problem, the think which I did to solve it was as follow

components/form/validationSchemaContext.js

import React from 'react'

const ValidationSchema = React.createContext({});

export const ValidationSchemaProvider = ValidationSchema.Provider;
export const ValidationSchemaConsumer = ValidationSchema.Consumer;

export default ValidationSchema;

components/form/index.js

import React from 'react';
import { FormContext } from 'react-hook-form';

import {ValidationSchemaProvider} from './validationSchemaContext';

export default ({className, methods, validationSchema, onSubmit, children}) => (
  <ValidationSchemaProvider value={validationSchema}>
    <FormContext {...methods} >
      <form className={className} onSubmit={methods.handleSubmit(onSubmit)}>
        {children}
      </form>
    </FormContext>
  </ValidationSchemaProvider>
);

components/input/index.js

import React, { useContext } from 'react';
import { useFormContext } from 'react-hook-form';

import ValidationSchemaContext from '../form/validationSchemaContext';

export default ({ name, ...props }) => {
      const { register, errors } = useFormContext();
      const validationSchema = useContext(ValidationSchemaContext);

      <input
        name={name}
        ref={register({
          validate: (val) => {
            return validationSchema.validateAt(name, {
              [name]: val
            })
            .then(() => true)
            .catch((e) => e.message)
          }
        })} />
});

Example usage

import React from 'react';
import * as Yup from 'yup';
import useForm from 'react-hook-form';

import Form from './components/form';
import Input from './components/input';

Yup.addMethod(Yup.string, 'customValidator', function() {
  return this.test({
    name: 'name',
    message: 'Input is not valid',
    test: (postcode = '') => {
      return fetch(...);
    }
  })
});

export default ({labels, className}) => {
  const validationSchema = Yup
    .object()
    .shape({
      input: Yup.string()
        .required()
        .customValidator()
    })

  const methods = useForm({
    mode: 'onBlur',
    reValidateMode: 'onChange'
  });

  const onSubmit = (values) => {};

  return (
    <Form methods={methods} onSubmit={onSubmit} validationSchema={validationSchema}>
      <Input
        name="input" />
      <button type="submit">Save</button>
    </Form>
  );
};

This way I have one validation schema but all inputs are validated separately. Changing value in one field does not trigger call to API to validate another field when its value was not changed.

It’s still not perfect as still blur event and submitting of form causes unnecessary validation. I think there is a need for some kind of memoization in validate function.

9reactions
bluebill1049commented, Nov 14, 2019

oh ok thanks, @giovanniantonaccio. Let’s leave this for a while to see if someone else facing a similar problem and then we can go from there. 👍

Read more comments on GitHub >

github_iconTop Results From Across the Web

How Does yup.addMethod() Work? Creating Custom ...
Yup's documentation is pretty vague about creating custom validation functions and the role of .addMethod() in it. This article will break it down...
Read more >
Async validation with Formik, Yup and React - Stack Overflow
You can use async/await with yup custom validator. let field = yup.string().test( 'test-id', 'error message in case of fail', async function ...
Read more >
How to Create Custom Form Validation in React with Yup
Creating custom form validation doesn't have to be hard. This tutorial will show you how to create custom form validation in React with...
Read more >
Form validation with Yup | Sanity.io guide
The primary aim of this article is to show you how to manage and validate forms in React using Formik and Yup. You...
Read more >
Validation with Yup | Techzaion Blog
Define object schema and its validation · Create validator object using Yup with expected schema and validation · Use Yup utility function "validate"...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found