Day13-React 表单验证篇-使用第三方函式库 Formik 进行表单的验证

Formik 介绍

在进行实作之前,先来认识一下 Formik 吧~

如标题所说,Formik 是一个表单函式库,而且还是 React 官方推荐的,相似的还有 Redux Form、React Final Form 等。并且 Formik 还可以搭配 Yup 撰写验证规则、讯息。

透过这些第三方的表单函式库可以帮助我们处理 Input 的事件追踪验证 Input value透过 React Context 管理 Form 的 state避免许多重复性值的程序码

Formik 的官网连结

实作时间

现在我们要来用 React 和 Formik 建立一个表单出来。

本篇教学从 Formik 官网文件的教学部分 改写而来

1. 简单做一个表单出来

const SimpleForm = () => {
  return (
    <form>
      <label htmlFor="name">Your Name</label>
      <input type="text" id="name" />
      <label htmlFor="email">Your E-Mail</label>
      <input type="email" id="email" />
      <button type="submit">Submit</button>
    </form>
  );
};

2. 使用 useFormik hook

透过这个 hook 去管理表单的 state 和一些函式。

import { useFormik } from "formik";

const SimpleForm = () => {
  const formik = useFormik({
    initialValues: {
      name: "",
      email: ""
    },
    onSubmit: (values) => {
      console.log(values);
    }
  });

  return (
    <form onSubmit={formik.handleSubmit}>
      <label htmlFor="name">Your Name</label>
      <input
        type="text"
        id="name"
        name="name"
        onChange={formik.handleChange}
        value={formik.values.name}
      />
      <label htmlFor="email">Your E-Mail</label>
      <input
        type="email"
        id="email"
        name="email"
        onChange={formik.handleChange}
        value={formik.values.email}
      />
      <button type="submit">Submit</button>
    </form>
  );
};

3. 加上验证函式和错误讯息

上一个步骤我们是可以直接送出表单看到值,现在我们针对表单输入内容进行验证,因此建立一个验证函式 validate。

将这个验证函式加入到 useFormik 的参数物件内,会在每次触发 onChange 和 onBlur 事件时做验证。

在後面的步骤,会改用 Yup 做验证

完成的表单元件如下:

import { useFormik } from "formik";

import "./styles.css";

const emailRule = /^\w+((-\w+)|(\.\w+))*\@[A-Za-z0-9]+((\.|-)[A-Za-z0-9]+)*\.[A-Za-z]+$/;

const validate = (values) => {
  const errors = {};

  if (!values.name) {
    errors.name = "Name must not be empty.";
  } else if (values.name.length > 15) {
    errors.name = "Must be 15 characters or less.";
  }

  if (!values.email) {
    errors.email = "Email must not be empty.";
  } else if (!emailRule.test(values.email)) {
    errors.email = "Please enter a valid email.";
  }

  return errors;
};

const SimpleForm = () => {
  const formik = useFormik({
    initialValues: {
      name: "",
      email: ""
    },
    validate,
    onSubmit: (values, { resetForm }) => {
      console.log(values);
      resetForm();
    }
  });

  const showNameError = formik.touched.name && formik.errors.name;
  const showEmailError = formik.touched.email && formik.errors.email;

  return (
    <form onSubmit={formik.handleSubmit}>
      <label htmlFor="name">Your Name</label>
      <input
        type="text"
        id="name"
        name="name"
        onChange={formik.handleChange}
        onBlur={formik.handleBlur}
        value={formik.values.name}
        className={showNameError ? "invalid" : ""}
      />
      {showNameError ? (
        <p className="error-text">{formik.errors.name}</p>
      ) : null}
      <label htmlFor="email">Your E-Mail</label>
      <input
        type="email"
        id="email"
        name="email"
        onChange={formik.handleChange}
        onBlur={formik.handleBlur}
        value={formik.values.email}
        className={showEmailError ? "invalid" : ""}
      />
      {showEmailError ? (
        <p className="error-text">{formik.errors.email}</p>
      ) : null}
      <button type="submit">Submit</button>
    </form>
  );
};

export default SimpleForm;

4. 改用 Yup 去验证表单输入值

虽然上个步骤的表单已经算完成了,不过我们来试试使用 Yup 吧!

将 validationSchema 和验证规则写在 useFormik 物件参数内即可。

import { useFormik } from "formik";
import * as Yup from "yup";

import "./styles.css";

const SimpleForm = () => {
  const formik = useFormik({
    initialValues: {
      name: "",
      email: ""
    },
    validationSchema: Yup.object({
      name: Yup.string()
        .max(15, "Must be 15 characters or less.")
        .required("Name must not be empty."),
      email: Yup.string()
        .email("Please enter a valid email.")
        .required("Email must not be empty.")
    }),
    onSubmit: (values, { resetForm }) => {
      console.log(values);
      resetForm();
    }
  });

  const showNameError = formik.touched.name && formik.errors.name;
  const showEmailError = formik.touched.email && formik.errors.email;

  return (
    <form onSubmit={formik.handleSubmit}>
      <label htmlFor="name">Your Name</label>
      <input
        type="text"
        id="name"
        name="name"
        onChange={formik.handleChange}
        onBlur={formik.handleBlur}
        value={formik.values.name}
        className={showNameError ? "invalid" : ""}
      />
      {showNameError ? (
        <p className="error-text">{formik.errors.name}</p>
      ) : null}
      <label htmlFor="email">Your E-Mail</label>
      <input
        type="email"
        id="email"
        name="email"
        onChange={formik.handleChange}
        onBlur={formik.handleBlur}
        value={formik.values.email}
        className={showEmailError ? "invalid" : ""}
      />
      {showEmailError ? (
        <p className="error-text">{formik.errors.email}</p>
      ) : null}
      <button type="submit">Submit</button>
    </form>
  );
};

export default SimpleForm;

5. 使用 getFieldProps() 稍微简化程序码

目前已经完成了表单的验证,不过会发现到每个输入栏都有一样的 onChange={formik.handleChange}onBlur={formik.handleBlur}等重复的程序码,这时就可以使用 getFieldProps() 去减少这些程序码。

// 原本的程序码
<input
  type="text"
  id="name"
  name="name"
  onChange={formik.handleChange}
  onBlur={formik.handleBlur}
  value={formik.values.name}
  className={showNameError ? "invalid" : ""}
/>

<input
  type="email"
  id="email"
  name="email"
  onChange={formik.handleChange}
  onBlur={formik.handleBlur}
  value={formik.values.email}
  className={showEmailError ? "invalid" : ""}
/>

// 加入 getFieldProps()
<input
  type="text"
  id="name"
  name="name"
  {...formik.getFieldProps("name")}
  className={showNameError ? "invalid" : ""}
/>
<input
  type="email"
  id="email"
  name="email"
  {...formik.getFieldProps("email")}
  className={showEmailError ? "invalid" : ""}
/>

6. 使用 Formik 元件取代 useFormik hook

在前面的实作,我们是透过 useFormik 去了解怎麽将表单加入验证功能,而现在我们要进行改写,会用到一个有使用到 React Context 的 Formik 的元件去取代 useFormik hook。

官网提供的关於 Formik 元件的程序码:

 import React from 'react';
 import { useFormik } from 'formik';
 
 // Create empty context
 const FormikContext = React.createContext({});
 
 // Place all of what’s returned by useFormik into context
 export const Formik = ({ children, ...props }) => {
   const formikStateAndHelpers = useFormik(props);
   return (
     <FormikContext.Provider value={formikStateAndHelpers}>
       {typeof children === 'function'
         ? children(formikStateAndHelpers)
         : children}
     </FormikContext.Provider>
   );
 };

以下是改写的结果,Formik 元件接受了一个函式做为它的 children:

import { Formik } from "formik";
import * as Yup from "yup";

import "./styles.css";

const SimpleForm = () => {
  return (
    <Formik
      initialValues={{ name: "", email: "" }}
      validationSchema={Yup.object({
        name: Yup.string()
          .max(15, "Must be 15 characters or less.")
          .required("Name must not be empty."),
        email: Yup.string()
          .email("Please enter a valid email.")
          .required("Email must not be empty.")
      })}
      onSubmit={(values, { resetForm }) => {
        console.log(values);
        resetForm();
      }}
    >
      {(formik) => (
        <form onSubmit={formik.handleSubmit}>
          <label htmlFor="name">Your Name</label>
          <input
            type="text"
            id="name"
            name="name"
            {...formik.getFieldProps("name")}
            className={
              formik.touched.name && formik.errors.name ? "invalid" : ""
            }
          />
          {formik.touched.name && formik.errors.name ? (
            <p className="error-text">{formik.errors.name}</p>
          ) : null}
          <label htmlFor="email">Your E-Mail</label>
          <input
            type="email"
            id="email"
            name="email"
            {...formik.getFieldProps("email")}
            className={
              formik.touched.email && formik.errors.email ? "invalid" : ""
            }
          />
          {formik.touched.email && formik.errors.email ? (
            <p className="error-text">{formik.errors.email}</p>
          ) : null}
          <button type="submit">Submit</button>
        </form>
      )}
    </Formik>
  );
};

export default SimpleForm;

注意把 useFormik 的参数物件移到 Formik 元件内时要改成 JSX 语法

7. 加入 Field、Form、ErrorMessage 元件

最後一个步骤,加入一些 formik 提供的元件,让程序码再次变更精简!

import { Formik, Field, Form, ErrorMessage } from "formik";
import * as Yup from "yup";

import "./styles.css";

const SimpleForm = () => {
  return (
    <Formik
      initialValues={{ name: "", email: "" }}
      validationSchema={Yup.object({
        name: Yup.string()
          .max(15, "Must be 15 characters or less.")
          .required("Name must not be empty."),
        email: Yup.string()
          .email("Please enter a valid email.")
          .required("Email must not be empty.")
      })}
      onSubmit={(values, { resetForm }) => {
        console.log(values);
        resetForm();
      }}
    >
      {(formik) => (
        <Form onSubmit={formik.handleSubmit}>
          <label htmlFor="name">Your Name</label>
          <Field
            name="name"
            render={({ field, meta }) => (
              <input
                type="text" {...field}
                className={meta.error ? "invalid" : ""}
              />
            )}
          />
          <ErrorMessage name="name">
            {(err) => <p className="error-text">{err}</p>}
          </ErrorMessage>
          <label htmlFor="email">Your E-Mail</label>
          <Field
            name="email"
            render={({ field, meta }) => (
              <input
                type="text" {...field}
                className={meta.error ? "invalid" : ""}
              />
            )}
          />
          <ErrorMessage name="email">
            {(err) => <p className="error-text">{err}</p>}
          </ErrorMessage>
          <button type="submit">Submit</button>
        </Form>
      )}
    </Formik>
  );
};

export default SimpleForm;

这样就完成了所有的实作,底下附上放在 codesandbox 上的程序码。

范例程序码(codesandbox)


<<:  【Day27】React Redux 原理及应用方法简介 ╰(°ㅂ°)╯

>>:  Day13|【Git】档案管理 - 档案还原 git checkout

D13 - 用 Swift 和公开资讯,打造投资理财的 Apps { 加权指数K线图实作.1 }

目标: 做出台湾加权指数 K 线图 之前做出来的台股申购是独立的功能,为了不影响前面已经完成的功能,...

Day30 Redis架构实战-Redis Request Routing/效能监控与调教

Redis Request Routing 在Redis Server丛集中所有的操作透过Reque...

#9 CSS3 Flexbox: main style setting

What is Flexbox? Flexbox = Flexible Box "CSS3...

[Day26] VSCode Plugin - Edge Tools<未完>

Show the browser's Elements and Network tool insi...

[Day5] MacOS - 打造美观的终端机画面

程序开发的过程很难不接触到终端机操作,原生的画面非常"简约",透过套件强化後不只...