⚛️

formikとyupでReactのFormを作成

2021/11/21に公開

概要

formik を使って、React の form を作成方法についてまとめます。

formik は React で Form を作成するときに便利なライブラリです。
初期値やバリデーションを React らしく作成可能です。
また Yup というバリデーションを簡潔に記述できるライブラリをサポートしており、組み合わせることで直感的な Form 作成を実現できます。

formik はチュートリアル(リンク)がとてもわかりやすいので、そちらをお勧めします。
本記事もチュートリアルの内容を参考にしてソースコードを記述します。

本記事のサンプルコードは以下です。
https://github.com/Msksgm/react-formik-example

実装

formik にはuseFormikFormikを用いた記述方法があります。
useFormikは Hooks を使用した実装方法で、Formikは API コンポーネントを使用した実装方法です。

useFormik Yup なし

useFormik を用いたときのサンプルコードです。

引数にinitialValues(初期値)、validate(バリデーション)、onSubmit(クリック時の挙動)を渡します。
validateは Yup を使わないと関数(最大値、最小値、有効な文字、エラーメッセージなど)を自作することになります。

import { FC } from "react";
import { useFormik } from "formik";

type valuesType = {
  firstName: string;
  lastName: string;
  email: string;
};

type errorsType = {
  firstName?: string;
  lastName?: string;
  email?: string;
};

const validate = (values: valuesType) => {
  const errors: errorsType = {};

  if (!values.firstName) {
    errors.firstName = "Required";
  } else if (values.firstName.length > 15) {
    errors.firstName = "Must be 15 characters or less";
  }

  if (!values.lastName) {
    errors.lastName = "Required";
  } else if (values.lastName.length > 20) {
    errors.lastName = "Must be 20 characters or less";
  }

  if (!values.email) {
    errors.email = "Required";
  } else if (!/^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}$/i.test(values.email)) {
    errors.email = "Invalid email address";
  }

  return errors;
};

const SignupForm: FC = () => {
  const formik = useFormik({
    initialValues: {
      firstName: "",
      lastName: "",
      email: "",
    },
    validate,
    onSubmit: (values) => {
      alert(JSON.stringify(values, null, 2));
    },
  });
  return (
    <form onSubmit={formik.handleSubmit}>
      <div>
        <label htmlFor="firstName">First Name</label>
        <input
          id="firstName"
          name="firstName"
          type="text"
          onChange={formik.handleChange}
          onBlur={formik.handleBlur}
          value={formik.values.firstName}
        />
        {formik.touched.firstName && formik.errors.firstName ? (
          <div>{formik.errors.firstName}</div>
        ) : null}
      </div>

      <div>
        <label htmlFor="lastName">Last Name</label>
        <input
          id="lastName"
          name="lastName"
          type="text"
          onChange={formik.handleChange}
          onBlur={formik.handleBlur}
          value={formik.values.lastName}
        />
        {formik.touched.lastName && formik.errors.lastName ? (
          <div>{formik.errors.lastName}</div>
        ) : null}
      </div>

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

      <button type="submit">Submit</button>
    </form>
  );
};

export default SignupForm;

useFormik Yup あり

useFormikYup を組み合わせたときのサンプルコードです。

引数のvalidationvalidationSchemaに変更され、Yupオブジェクトを直接渡すことが可能です。
バリデーションを自作するよりも簡潔に記述できるようになりました。

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

const SignupForm: FC = () => {
  const formik = useFormik({
    initialValues: {
      firstName: "",
      lastName: "",
      email: "",
    },
    validationSchema: Yup.object({
      firstName: Yup.string()
        .max(15, "Must be 15 characters or less")
        .required("Required"),
      lastName: Yup.string()
        .max(20, "Must be 20 characters or less")
        .required("Required"),
      email: Yup.string().email("Invalid email address").required("Required"),
    }),
    onSubmit: (values) => {
      alert(JSON.stringify(values, null, 2));
    },
  });
  return (
    <form onSubmit={formik.handleSubmit}>
      <label htmlFor="firstName">First Name</label>
      <input
        id="firstName"
        name="firstName"
        type="text"
        onChange={formik.handleChange}
        onBlur={formik.handleBlur}
        value={formik.values.firstName}
      />
      {formik.touched.firstName && formik.errors.firstName ? (
        <div>{formik.errors.firstName}</div>
      ) : null}

      <label htmlFor="lastName">Last Name</label>
      <input
        id="lastName"
        name="lastName"
        type="text"
        onChange={formik.handleChange}
        onBlur={formik.handleBlur}
        value={formik.values.lastName}
      />
      {formik.touched.lastName && formik.errors.lastName ? (
        <div>{formik.errors.lastName}</div>
      ) : null}

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

      <button type="submit">Submit</button>
    </form>
  );
};

export default SignupForm;

Formik Yup あり

Formikを用いたサンプルコードです。
FieldFormErrorMessageといった関数も formik に用意されており、これらを組み合わせることでuseFormikを使うよりもさらに簡潔に書くことが可能です。

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

const SignupForm: FC = () => {
  return (
    <Formik
      initialValues={{ firstName: "", lastName: "", email: "" }}
      validationSchema={Yup.object({
        firstName: Yup.string()
          .max(15, "Must be 15 characters or less")
          .required("Required"),
        lastName: Yup.string()
          .max(20, "Must be 20 characters or less")
          .required("Required"),
        email: Yup.string().email("Invalid email address").required("Required"),
      })}
      onSubmit={(values, { setSubmitting }) => {
        setTimeout(() => {
          alert(JSON.stringify(values, null, 2));
          setSubmitting(false);
        }, 400);
      }}
    >
      <Form>
        <div>
          <label htmlFor="firstName">First Name</label>
          <Field name="firstName" type="text" />
          <ErrorMessage name="firstName" />
        </div>

        <div>
          <label htmlFor="lastName">Last Name</label>
          <Field name="lastName" type="text" />
          <ErrorMessage name="lastName" />
        </div>

        <div>
          <label htmlFor="email">Email Address</label>
          <Field name="email" type="email" />
          <ErrorMessage name="email" />
        </div>

        <button type="submit">Submit</button>
      </Form>
    </Formik>
  );
};

export default SignupForm;

動作確認

実行

git clone https://github.com/Msksgm/react-formik-example.git
cd react-formik-example
yarn
yarn start

画面

localhost:3000に以下のような画面が表示されます。
すべての Form が同じ動作をしますが、<Formik />を使用することでソースコードの記述量が激減します。
ですので、保守性の観点も含めて、formik を使うときには<Formik />を使うようにしています。
checkbox を使うときの書き方もチュートリアルに記述してあるので参考にしてみてください。


localhost3000

参考

https://formik.org/docs/tutorial

Discussion