チンパンジーでもわかるようにformikを説明

目次

Formikとは

formikは簡単にいうと、ReactやReact NativeでFormを作るときにformikを使えば簡単に作れますよというオープンソースのライブラリです。

もっと言えば、フォームを作るだけなら<input>タグなどでできますが入力された値に想定された値が入っているかどうかなどをチェックするのは、いちいち実装してたらめんどくさい。

そのためのFormikです!

インストール

使用されているパッケージマネージャーに合わせてインストールしてください。

npm

npm install formik --save

yarn

yarn add formik

CDN

パッケージマネージャーを使用されていない場合は、以下のスクリプトを記載することでも可能です。

<script src="https://unpkg.com/formik/dist/formik.umd.production.min.js"></script>

また筆者は以下の環境で使用いたします。

Typescript version : 4.6.3
formik: 2.2.9
material ui: 5.8.0

使用例

Typescriptで書いたサンプルは以下になります。

import React from "react";
import "./App.css";
import { Formik } from "formik";
import { Button, Stack, Typography } from "@mui/material";

const App: React.FC = () => {
  type ErrorType = {
    email?: string;
    password?: string;
  };
  return (
    <React.Fragment>
      <Stack p={5} gap={2} width="50%">
        <Typography variant="h3">This is the formik</Typography>
        <Formik
          initialValues={{ email: "", password: "" }}
          validate={(values) => {
            const errors: ErrorType = {};
            if (!values.email) {
              errors.email = "required";
            } else if (
              !/^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$/i.test(values.email)
            ) {
              errors.email = "invalid email address";
            }

            if (!values.password) {
              errors.password = "required";
            }

            return errors;
          }}
          onSubmit={(values, actions) => {
            alert(values.email + values.password);
            return;
          }}
        >
          {({
            values,
            errors,
            touched,
            handleSubmit,
            isSubmitting,
            handleChange,
            handleBlur,
          }) => (
            <form onSubmit={handleSubmit}>
              <Stack sx={{ gap: 3 }}>
                <input
                  id="email"
                  name="email"
                  type="text"
                  onChange={handleChange}
                  onBlur={handleBlur}
                  value={values.email}
                />
                {touched.email && errors.email ? (
                  <div>{errors.email}</div>
                ) : null}
                <input
                  id="password"
                  name="password"
                  type="password"
                  onChange={handleChange}
                  onBlur={handleBlur}
                  value={values.password}
                />
                {touched.password && errors.password ? (
                  <div>{errors.password}</div>
                ) : null}
                <Button
                  variant="contained"
                  type="submit"
                  disabled={isSubmitting}
                >
                  Submit
                </Button>
              </Stack>
            </form>
          )}
        </Formik>
      </Stack>
    </React.Fragment>
  );
};

export default App;

上記のコードを入力すると、以下のようなフォームの形になります。

それではどのコードがどんな役割をしているか見ていきましょう。

initialValues

initialValuesではformikで取り扱うデータの初期値を定義いたします。

例えば以下のようにコードを書いたとき

initialValues={{ email: "sample@example.com", password: "" }

この状態でページを更新すると以下のようにフォームに値が入った状態になります。

onSubmit

onSubmitではformのsubmitボタンを押したときに何を実施するかの処理を記載いたします。

onSubmitでフォームに入力された値を使用する際は、onSubmitで定義された第一引数をvaluesなどのようにすることでformikの値にアクセスすることができます。

          onSubmit={(values, actions) => {
            alert(values.email + values.password);
            return;
          }}

上記ではサブミットのボタンが押下されたタイミングで、アラートにメールアドレスやパスワードを乗せるような処理です。
実際にはここでapiの呼び出しや、エラーハンドリングなどを実装するイメージなります。

isSubmitting

isSubmittingは簡単にいうと処理が今走っているのか?ということがわかる変数です。

isSubmittingはBoolean型で、デフォルト値はfalseになっています。

こちらはonSubmitが走ったタイミングでtrueになります。

そのため例えば処理が走ったタイミングで再度ボタンを押せないようにフロントで制御するときは、buttonのdisabledにisSubmittingを渡してあげます。

こういった機能が標準で備わっていることがformikの何よりのメリットかもしれません。

<Button
      variant="contained"
      type="submit"
      disabled={isSubmitting}
>

ボタンを押すと無効化される。

ではどうやってisSubmittingを元のfalseにするかというと、これは自分で制御しないといけないようです。

公式ドキュメントにも以下の記載があります。

Formik公式

Submission
Proceed with running your submission handler (i.e.onSubmit or handleSubmit)
you call setSubmitting(false) in your handler to finish the cycle

setSubmittingを使用すれば可能です。

          onSubmit={(values, actions) => {
            alert(values.email + values.password);
            return;
          }}

onSubmitの定義の第二引数に変数を定義することで、formikのactionにアクセスすることが可能です。

actions.setSubmitting(false)を使用し、いい感じにisSubmittingを制御しましょう!

onChange

よく見てみると、Formikでは特にonChangeは定義してませんが、各コンポーネントではonChange={handleChange}と定義しているのがわかります。

こちらはReact特有のstateの変化を定義しているものになります。stateの説明はここでは省略いたします。
簡単にいうとReactはコンポーネントの状態をstateで管理しているために、onChangeを正しく渡してあげないとテキストフィールドに文字が打てないなどの自体になります。

特に以下のようなコードをしているものとイメージいただければと思います。

const [values, setValues] = useState();

const handleChange = event => {
  setValues(prevValues => ({
    ...prevValues,
    [event.target.name]: event.target.value
  }));
}

上記は公式にも記載されています。

event.target.nameの箇所でformikで定義したvalueを随時渡しているという状態です。

わざわざFormikで定義した値をそれぞれuseStateしなくても、handleChangeを使用すればいいのは便利ですね。

validate

validateは入力された値がどんな状態かを検証する役割があります。

以下のvalidateでは値が空欄であるかや正しいメールアドレスであるかを検証し、指定した条件にマッチした場合に合わせてerrosにエラーメッセージを格納しています。

          validate={(values) => {
            const errors: ErrorType = {};
            if (!values.email) {
              errors.email = "required";
            } else if (
              !/^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$/i.test(values.email)
            ) {
              errors.email = "invalid email address";
            }

            if (!values.password) {
              errors.password = "required";
            }

            return errors;
          }}

errorsがある場合は以下のようにメッセージを表示します。

                {errors.email}

validateが機能されるタイミングはformikでは以下のタイミングです。

  • キーがフォームで入力されたタイミング(change event)
  • カーソルが外れたタイミング(blur event)
  • submitされたタイミング

そのためわざわざフォームが送信されたときに再度バリデーションを全体で実装する必要がなくなります。便利!

touched ・ handleBlur

touchedではユーザーがこのインプットフォームを触ったかどうかを判定します。

handleBlurをインプットフォームに設定することによってformikにこのフォームを触ったよ〜とハンドリングしてくれるようになります。

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

なぜこのようなことが必要かと言うと、例えば上記を正しく設定されていなかった場合メールアドレスでバリデーションエラーが発生したタイミングでパスワードのバリデーションエラーも表示されてしまうというバグが発生します。

// touchedがない場合 
{errors.password ? <div>{errors.password}</div> : null}

メールアドレスしか入力していないのにパスワードのバリデーションエラーが発生起動してしまう

そのためにユーザーがフォームを入力したかどうかを適切にハンドリングするためにonBlurの設定が必要になります。

コード量削減

Yup

バリデーションロジックを毎度書くのはめんどくさいので、使えるのであれば3rdパーティー製のライブラリYupを使用しましょう。

npm

npm install -S yup

yarn

yarn add Yup

Yupを使用することでバリデーションのロジックが以下だけですみます。

          validationSchema={Yup.object({
            email: Yup.string()
              .email("Invalid email address")
              .required("required"),
            password: Yup.string().required("required"),
          })}

Field Error Form

毎度inputタグ内にonBlurとか設置するのも、少量ならいいのですが大量にフォームがあった場合改修するだけで一苦労になります。

Formikで既に用意されているFieldコンポーネントを使用すればさらに楽になります。

結論から言うと使用例で示していたformが以下のように変更可能です。

          <Form>
            <Stack sx={{ gap: 3 }}>
              <Field name="email" type="text" />
              <ErrorMessage name="email" />
              <Field name="password" type="password" />
              <ErrorMessage name="password" />
              <Button variant="contained" type="submit">
                Submit
              </Button>
            </Stack>
          </Form>

上記コードでも同様にフォームが機能します。

まとめ

formikは便利ですね。

今回はformikの基礎的なことに触れましたが、深追いしていこうかなとも思います。

よかったらシェアしてね!
  • URLをコピーしました!

この記事を書いた人

数学科出身のSoftware Engineer
情報通信が好きなのでブログを活用して発信しています。

コメント

コメントする

目次
閉じる