Lädt...

🔧 Formik & React (Part 3): Validation with yup & Streamlining Code


Nachrichtenbereich: 🔧 Programmierung
🔗 Quelle: dev.to

So far, we have managed the state of our order submission form, validated our form fields, displayed error messages,handled form submission, and tracked visited fields. However, there is an even better way to carry out form validation, and that is through the use of a library known as yup. With yup, we can eliminate manual interference from our validate function.

Validation with yup

We start by installing yup and importing it into our OrdersForm component. yup allows us to define an object schema validation that can be assigned to a yup object schema. This object will contain the validation rules for our form fields (name, email, and quantity).

import "./order.css"
import { useFormik } from "formik"
import * as Yup from "yup"


const initialValues = {
  name: "",
  email: "",
  quantity: 0
}

const onSubmit = (values) => {
  console.log(values)
}


const validationSchema = Yup.object({
  name: Yup.string().required("Name is required"),
  email: Yup.string().email("Invalid email format").required("Email is required"),
  quantity: Yup.number().required("Quantity is required")
})

After this, the schema can be fed into the useFormik hook replacing the validate function that we previously used.

 const formik = useFormik({
    initialValues,
    onSubmit,
    validationSchema
  })

So, we can remove our custom validation and our updated code becomes:

import "./order.css"
import { useFormik } from "formik"
import * as Yup from "yup"


const initialValues = {
  name: "",
  email: "",
  quantity: 0
}

const onSubmit = (values) => {
  console.log(values)
}


const validationSchema = Yup.object({
  name: Yup.string().required("Name is required"),
  email: Yup.string().email("Invalid email format").required("Email is required"),
  quantity: Yup.number().required("Quantity is required")
})


export const OrdersForm = () => {
  const formik = useFormik({
    initialValues,
    onSubmit,
    validationSchema
  })


  return (
    <div className="form-container">
      <h2>Order Submission Form</h2>
      <form onSubmit={formik.handleSubmit}>
        <div className="form-group">
          <label htmlFor='name'>Name:</label>
          <input
            type="text"
            name="name"
            id='name'
            required
            onChange={formik.handleChange}
            onBlur={formik.handleBlur}
            value={formik.values.name}
          />
          {formik.touched.name && formik.errors.name ? (
            <div className="error">{formik.errors.name}</div>
          ) : null}
        </div>

        <div className="form-group">
          <label htmlFor='email'>Email:</label>
          <input
            type="email"
            name="email"
            id='email'
            required
            onChange={formik.handleChange}
            onBlur={formik.handleBlur}
            value={formik.values.email}
          />
          {formik.touched.email && formik.errors.email ? (
            <div className="error">{formik.errors.email}</div>
          ) : null}
        </div>

        <div className="form-group">
          <label htmlFor='quantity'>Order Quantity:</label>
          <input
            type="number"
            name="quantity"
            id='quantity'
            required
            onChange={formik.handleChange}
            onBlur={formik.handleBlur}
            value={formik.values.quantity}
          />
          {formik.touched.quantity && formik.errors.quantity ? (
            <div className="error">{formik.errors.quantity}</div>
          ) : null}
        </div>

        <button type="submit" className="submit-btn">
          Submit Order
        </button>
      </form>
    </div>
  )
}


Condensing logic and Refactoring for clarity

Although our form works as it should at the moment, it is possible to optimize our code structure and minimize redundancy. If we closely examine our name, email, and quantity form fields, we see that several props (onChange, onBlur, and value) are being repeated. This repetition violates the DRY (Don't Repeat Yourself) principle. To improve efficiency, we can abstract the common configuration from each field in our form.

A more streamlined approach is to use a helper method that dynamically applies these props as needed. Formik provides the formik.getFieldProps() method, which simplifies this process by accepting the field's name as its argument and automatically handling its state and events.

import "./order.css"
import { useFormik } from "formik"
import * as Yup from "yup"


const initialValues = {
  name: "",
  email: "",
  quantity: 0
}

const onSubmit = (values) => {
  console.log(values)
}

const validationSchema = Yup.object({
  name: Yup.string().required("Name is required"),
  email: Yup.string().email("Invalid email format").required("Email is required"),
  quantity: Yup.number().required("Quantity is required")
})


export const OrdersForm = () => {
  const formik = useFormik({
    initialValues,
    onSubmit,
    validationSchema
  })

  return (
    <div className="form-container">
      <h2>Order Submission Form</h2>
      <form onSubmit={formik.handleSubmit}>
        <div className="form-group">
          <label htmlFor='name'>Name:</label>
          <input
            type="text"
            name="name"
            id='name'
            {...formik.getFieldProps('name')}
          />
          {formik.touched.name && formik.errors.name ? (
            <div className="error">{formik.errors.name}</div>
          ) : null}
        </div>

        <div className="form-group">
          <label htmlFor='email'>Email:</label>
          <input
            type="email"
            name="email"
            id='email'
            {...formik.getFieldProps('email')}

          />
          {formik.touched.email && formik.errors.email ? (
            <div className="error">{formik.errors.email}</div>
          ) : null}
        </div>

        <div className="form-group">
          <label htmlFor='quantity'>Order Quantity:</label>
          <input
            type="number"
            name="quantity"
            id='quantity'
            required
            {...formik.getFieldProps('quantity')}
          />
          {formik.touched.quantity && formik.errors.quantity ? (
            <div className="error">{formik.errors.quantity}</div>
          ) : null}
        </div>

        <button type="submit" className="submit-btn">
          Submit Order
        </button>
      </form>
    </div>
  )
}

So, three lines of code in each form field is replaced with a single line.

While the formik.getFieldProps() helper method helps reduce redundant code, we still need to manually pass it to each input field. This means that if our form has ten fields, we would have to call formik.getFieldProps() ten times, which can become repetitive.

To simplify this further, Formik offers built-in components that help reduce verbosity and improve readability. These include the Formik, Form, Field, and ErrorMessage components, which streamline form handling and validation.

The Formik component can serve as a replacement for the useFormik hook in our code. Previously, we passed initialValues, onSubmit, and validationSchema as an object argument within the useFormik hook. Now, instead of using useFormik, we will pass these configurations as props directly to the Formik component.

Before making this change, we need to update our imports by replacing useFormik with Formik.

import "./order.css"
import { Formik } from "formik"
import * as Yup from "yup"

Next, we remove the useFormik call and wrap our entire form with the Formik component, passing the necessary props directly to it.

import "./order.css"
import { Formik } from "formik"
import * as Yup from "yup"


const initialValues = {
  name: "",
  email: "",
  quantity: 0
}

const onSubmit = (values) => {
  console.log(values)
}

const validationSchema = Yup.object({
  name: Yup.string().required("Name is required"),
  email: Yup.string().email("Invalid email format").required("Email is required"),
  quantity: Yup.number().required("Quantity is required")
})


export const OrdersForm = () => {
  return (
    <Formik initialValues={initialValues} validationSchema={validationSchema} onSubmit={onSubmit}>
      <div className="form-container">
        <h2>Order Submission Form</h2>
        <form onSubmit={formik.handleSubmit}>
          <div className="form-group">
            <label htmlFor='name'>Name:</label>
            <input
              type="text"
              name="name"
              id='name'
              {...formik.getFieldProps('name')}
            />
            {formik.touched.name && formik.errors.name ? (
              <div className="error">{formik.errors.name}</div>
            ) : null}
          </div>

          <div className="form-group">
            <label htmlFor='email'>Email:</label>
            <input
              type="email"
              name="email"
              id='email'
              {...formik.getFieldProps('email')}

            />
            {formik.touched.email && formik.errors.email ? (
              <div className="error">{formik.errors.email}</div>
            ) : null}
          </div>

          <div className="form-group">
            <label htmlFor='quantity'>Order Quantity:</label>
            <input
              type="number"
              name="quantity"
              id='quantity'
              required
              {...formik.getFieldProps('quantity')}
            />
            {formik.touched.quantity && formik.errors.quantity ? (
              <div className="error">{formik.errors.quantity}</div>
            ) : null}
          </div>

          <button type="submit" className="submit-btn">
            Submit Order
          </button>
        </form>
      </div>
    </Formik>
  )
}

We need to wrap the entire form with the Formik component to enable the use of additional components that simplify our form code. This allows us to introduce the Form component along with other necessary Formik components.

Next, we import the Form component and replace the standard HTML <form> element with it. Additionally, we remove the onSubmit prop from the Form tag. Internally, the Form component acts as a wrapper around the native <form> element, automatically integrating with Formik’s handleSubmit method.

import "./order.css"
import { Formik, Form } from "formik"
import * as Yup from "yup"


const initialValues = {
  name: "",
  email: "",
  quantity: 0
}

const onSubmit = (values) => {
  console.log(values)
}

const validationSchema = Yup.object({
  name: Yup.string().required("Name is required"),
  email: Yup.string().email("Invalid email format").required("Email is required"),
  quantity: Yup.number().required("Quantity is required")
})


export const OrdersForm = () => {
  return (
    <Formik initialValues={initialValues} validationSchema={validationSchema} onSubmit={onSubmit}>
      <div className="form-container">
        <h2>Order Submission Form</h2>
        <Form>
          <div className="form-group">
            <label htmlFor='name'>Name:</label>
            <input
              type="text"
              name="name"
              id='name'
              {...formik.getFieldProps('name')}
            />
            {formik.touched.name && formik.errors.name ? (
              <div className="error">{formik.errors.name}</div>
            ) : null}
          </div>

          <div className="form-group">
            <label htmlFor='email'>Email:</label>
            <input
              type="email"
              name="email"
              id='email'
              {...formik.getFieldProps('email')}

            />
            {formik.touched.email && formik.errors.email ? (
              <div className="error">{formik.errors.email}</div>
            ) : null}
          </div>

          <div className="form-group">
            <label htmlFor='quantity'>Order Quantity:</label>
            <input
              type="number"
              name="quantity"
              id='quantity'
              required
              {...formik.getFieldProps('quantity')}
            />
            {formik.touched.quantity && formik.errors.quantity ? (
              <div className="error">{formik.errors.quantity}</div>
            ) : null}
          </div>

          <button type="submit" className="submit-btn">
            Submit Order
          </button>
        </Form>
      </div>
    </Formik>
  )
}

Next, we introduce the Field component to simplify form field handling. Currently, we are using the getFieldProps helper method for each field, passing its corresponding name as an argument. However, we can further abstract this process to make our code cleaner and more efficient.

To achieve this, we import Field from Formik and replace our existing <input> elements with the Field component. This allows us to remove the getFieldProps helper method from each field, streamlining our form implementation. Our updated code now looks like this:

import "./order.css"
import { Formik, Form, Field } from "formik"
import * as Yup from "yup"


const initialValues = {
  name: "",
  email: "",
  quantity: 0
}

const onSubmit = (values) => {
  console.log(values)
}

const validationSchema = Yup.object({
  name: Yup.string().required("Name is required"),
  email: Yup.string().email("Invalid email format").required("Email is required"),
  quantity: Yup.number().required("Quantity is required")
})


export const OrdersForm = () => {
  return (
    <Formik initialValues={initialValues} validationSchema={validationSchema} onSubmit={onSubmit}>
      <div className="form-container">
        <h2>Order Submission Form</h2>
        <Form>
          <div className="form-group">
            <label htmlFor='name'>Name:</label>
            <Field
              type="text"
              name="name"
              id='name'
            />
            {formik.touched.name && formik.errors.name ? (
              <div className="error">{formik.errors.name}</div>
            ) : null}
          </div>

          <div className="form-group">
            <label htmlFor='email'>Email:</label>
            <Field
              type="email"
              name="email"
              id='email'
            />
            {formik.touched.email && formik.errors.email ? (
              <div className="error">{formik.errors.email}</div>
            ) : null}
          </div>

          <div className="form-group">
            <label htmlFor='quantity'>Order Quantity:</label>
            <Field
              type="number"
              name="quantity"
              id='quantity'
              required
            />
            {formik.touched.quantity && formik.errors.quantity ? (
              <div className="error">{formik.errors.quantity}</div>
            ) : null}
          </div>

          <button type="submit" className="submit-btn">
            Submit Order
          </button>
        </Form>
      </div>
    </Formik>
  )
}

Our form errors are still referencing formik, which was previously invoked through the useFormik hook that we have now removed. To address this, we will use the ErrorMessage component.

Currently, we are manually checking whether a field has been visited and whether an error exists before displaying the error message. Since we repeat this process for all three form fields, it introduces unnecessary redundancy. The ErrorMessage component helps eliminate this repetition.

As with the other Formik components, we first import ErrorMessage from Formik. Then, we replace our existing error messages with the ErrorMessage component, passing a name prop that matches the corresponding Field component’s name attribute.

We've successfully reduced the amount of code significantly and introduced yup for creating a validation schema. In the next article, we'll explore how to disable the submit button, load saved data, and reset the form data.

...

🔧 Formik &amp; React (Part 3): Validation with yup &amp; Streamlining Code


📈 84.62 Punkte
🔧 Programmierung

🔧 How to Validate Forms in React and React Native Using Yup and Formik


📈 57.46 Punkte
🔧 Programmierung

🔧 Using Formik- Beginner’s Guide to Connecting a React Formik Form to a Flask Backend


📈 55.31 Punkte
🔧 Programmierung

🔧 Converting React Forms to Formik and Yup


📈 51.78 Punkte
🔧 Programmierung

🔧 React forms: Formik and Yup intro


📈 51.78 Punkte
🔧 Programmierung

🔧 Formik &amp; Yup simplified:


📈 47.49 Punkte
🔧 Programmierung

🔧 How To Use Formik &amp; Yup For Form Validations


📈 47.49 Punkte
🔧 Programmierung

🔧 Handling Complex Multi Step Forms with Formik and Yup


📈 46.1 Punkte
🔧 Programmierung

🔧 yup.ref is not working inside the yup.date().min


📈 42.57 Punkte
🔧 Programmierung

🔧 From Zero to Hero: Build React Forms with Validation Using React Hook Form and Yup


📈 39.33 Punkte
🔧 Programmierung

🔧 Comparing Formik and React Hook Form: Which One Should You Choose for Your React Forms?


📈 36.17 Punkte
🔧 Programmierung

🔧 Crafting Forms in React: Vanilla vs. React Hook Form vs. Formik


📈 36.17 Punkte
🔧 Programmierung

🔧 Setup Validation Pipes &amp; Yup for NestJS Request Validation.


📈 36.05 Punkte
🔧 Programmierung

🔧 How to use Yup validation with React Hook Form


📈 33.65 Punkte
🔧 Programmierung

🔧 Building Forms in React Native with React Hook Form and Yup


📈 32.64 Punkte
🔧 Programmierung

🔧 Building a CRUD App with Next.js, React Query, React Hook Form, and Yup


📈 32.64 Punkte
🔧 Programmierung

🔧 Formik &amp; React: Writing Cleaner, More Efficient Forms


📈 31.88 Punkte
🔧 Programmierung

🔧 How To Verify Forms Using Formik in React js


📈 30.49 Punkte
🔧 Programmierung

🔧 React-Hook-Form vs Formik: The Good, Bad, and Ugly


📈 30.49 Punkte
🔧 Programmierung

🔧 Formik vs. React Hook Form


📈 30.49 Punkte
🔧 Programmierung

🔧 How to Build React Forms with Formik


📈 30.49 Punkte
🔧 Programmierung

🔧 Streamlining IT Streamlining Infrastructure: The Power of Red Hat Linux and Ansible Automation


📈 28.24 Punkte
🔧 Programmierung

🔧 schema-env v2.1: Now with Pluggable Validation Adapters (Joi, Yup, Your Own!)


📈 27.97 Punkte
🔧 Programmierung

🔧 Zod vs Yup: Choosing the Right Validation Library for Your Frontend Project


📈 27.97 Punkte
🔧 Programmierung

🔧 Dynamically Generating Interfaces and Validation Schemas in TypeScript with Yup


📈 27.97 Punkte
🔧 Programmierung

🔧 Form validation with YUP


📈 27.97 Punkte
🔧 Programmierung

🔧 Yup: The efficient validation schema handler


📈 27.97 Punkte
🔧 Programmierung

🔧 Using yup to build schema with value parsing and validation.


📈 27.97 Punkte
🔧 Programmierung

🔧 Validation With Yup!. Did You Know This Method ?


📈 27.97 Punkte
🔧 Programmierung

🔧 Client-side object validation with Yup


📈 27.97 Punkte
🔧 Programmierung

🔧 Building a Complex Form with React Hook Form and Yup


📈 26.96 Punkte
🔧 Programmierung

🔧 Getting Started with Yup Validations in React: A Beginner's Guide


📈 26.96 Punkte
🔧 Programmierung

🔧 How I React Hook Form with Yup and TypeScript


📈 26.96 Punkte
🔧 Programmierung

🔧 Introducing react-tools: A Toolbox for Streamlining React Development


📈 25.47 Punkte
🔧 Programmierung