FormikでReactのformを簡単に扱おう！

# はじめに
この記事は[POL AdventCalender](https://qiita.com/advent-calendar/2019/pol)の8日目の記事です。

こんにちは！[株式会社POL](https://pol.co.jp/)でフロントエンドエンジニアをしております、橋本ことブリボンと申します！
弊社では、フロントエンドはReactを用いて開発をしております。

みなさんは、Reactでformを作る際、どのように実装しますか？
ライブラリーを使わずに実装する、Formikを使う、redux-formを使うなどなど……
弊社では、Formikを使ってformの実装しております。

また、formのバリデーションを実装するために、 **Yup** というライブラリーを使っているのですが、Formikの説明だけに注力したいため、詳細な説明は致しません。
別の記事で、Yupを使ったformのバリデーションの解説をしようと思っております。

# Formikとは何か？
Formikとは、 Reactでformをより簡単で簡潔に実装できるようにしてくれるライブラリーです。
[公式ドキュメント](https://jaredpalmer.com/formik/)

Formikの特徴として、
1. formのvalueを管理してくれる
2. エラーのハンドリングと管理もできる
3. バリデーションを簡単に設定できる
4. onChangeやonSubmitなどのイベントハンドラを提供してくれる
5. idもしくは、name属性を与えることで、どの要素を変更したのかの検知ができる

主に、上記5つの特徴を備えています。
早速、実装の解説に移りましょう。
わかりやすくするために基本編では、ログインフォームのフロントだけの実装を例にして解説していきます。
※reduxは一切出しません。
必要な要素は、メールアドレスとパスワードのinput要素だけです。

# Formik[基本編]
## Formikとyupをプロジェクトへインストール
```
yarn add formik yup
```
これだけですね！

## Formikの実装
以下のコードはサンプルコードです。
メールアドレスのname属性は*email*、
パスワードのname属性は*password*と設定しています。

Formikでは、 **Formik**と呼ばれるコンポーネントにいくつかpropsを渡すことで、formの実装をします。

```Login.js
import React from "react";
import { Formik } from "formik";

const Login = () => {
  return (
     <Formik
       initialValues={{ email: "", password: "" }}
       onSubmit={values => console.log(values)}
       render={(props) => (
         <form onSubmit={props.handleSubmit}>
           <div>
             <label>email</label>
             <input
               name="email"
               value={props.values.email}
               onChange={props.handleChange}
             />
           </div>
           <div>
             <label>password</label>
             <input
               type="password"
               name="password"
               value={props.values.password}
               onChange={props.handleChange}
             />
           </div>
           <button type="submit">送信</button>
         </form>
       )}
     />
  );
};

export default Login;
```

### initialValues
読んで字の如く、**初期値**です。
デフォルトでは何も入らないことを想定していますので、空文字列を入れています。
※書きながら思いついたのですが、初期値が必要ない場合、initialValuesを定義する必要はないかもしれません。

仮に、以下のように初期値を渡すと、下の画像の通りになります。

```javascript

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

<img width="251" alt="Screen Shot 2019-12-08 at 13.52.43.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/261546/0664f335-99ca-816b-1e25-9ba5f31afe76.png">

### onSubmit
こちらもinitialValuesと同じく、名前から役割がわかるのではないでしょうか。
onSumbitイベントが発火した時に、どういう処理を行うか記述する箇所です。
**{}**の中は関数で定義し、引数にはformのvaluesを渡します。

とりあえず、ここではAPIを叩くということもしないので、consoleにデバッグしています。

formにメールアドレスとパスワードを入力し、「送信」ボタンを押すと、入力したものがデバッグされているのが確認できます。
<img width="675" alt="Screen Shot 2019-12-08 at 14.03.47.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/261546/700d5d14-80b2-42c5-6020-cb69611586a7.png">



### render
Formikの肝であり中身であるrender。
RenderPropsを使っていることもあり、少しわかりづらい実装となっております。

まず、renderはpropsを持ちます。
中身は、values,　errors,　handleChange,　handleSubmit,　handleBlur……などなど。
ここで必要なのは、valuesとerrorsとhandleChangeとhandleSubmitです。

valuesには、formに入力したvalueが入り、handleChangeとhandleSubmitはそれぞれ、formのvalueの変更と送信のハンドラーです。

formタグのonSubmitイベントにhandleSubmitを、formの要素のonChangeイベントにhandleChangeを渡すことで、それぞれのハンドラーを使用することができます。
valuesはformの要素に渡さなくても機能します。
初期値を何かしら渡す必要がある時にだけ、渡せば良いです。
※知識不足で申し訳ないですが、idかnameを指定するだけで初期値が入力されるかも。曖昧ですみません(><)

最初の方でも述べましたが、formの要素には**idかname属性、どちらかを指定してください。**
私は、name属性を指定する方を好みます。
name属性を指定することで、Formikがどの要素を変更したかを検知できます。

## バリデーションの実装
サンプルコードのみを提示します。
画像のようにエラーメッセージが表示されます。

```Login.js
import React from "react";
import styled from "styled-components";
import { Formik } from "formik";
import * as Yup from "yup";

const Error = styled.span`
  color: red;
`;

const validation = () =>
  Yup.object().shape({
    email: Yup.string()
      .email("メールアドレスの形式で入力してください")
      .required("必須項目です"),
    password: Yup.string().required("必須項目です")
  });

const Login = () => {
  return (
     <Formik
       initialValues={{ email: "", password: "" }}
       validationSchema={validation()}
       onSubmit={values => console.log(values)}
       render={(props) => (
         <form onSubmit={props.handleSubmit}>
           <div>
             <label>email</label>
             <input
               name="email"
               value={props.values.email}
               onChange={props.handleChange}
             />
             <Error>{props.errors.email}</Error>
           </div>
           <div>
             <label>password</label>
             <input
               type="password"
               name="password"
               value={props.values.password}
               onChange={props.handleChange}
             />
             <Error>{props.errors.password}</Error>
           </div>
           <button type="submit">送信</button>
         </form>
       )}
     />
  );
};

export default Login;
```

<img width="545" alt="Screen Shot 2019-12-08 at 15.39.08.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/261546/d15e6c39-7091-eda4-bcec-ab1e0055edb6.png">


駆け足ではありますが、以上がFormikの基本的な使い方です。
以降ではもう一歩踏み込んで、再利用可能性を高めるためのコンポーネント分割を行っていきます。
そのために、必要なメソッドがあるので順次解説していきます。

# Formik[ちょっと応用編]
## Formikの悩み
ログインフォームでは、基本的にメールアドレスとパスワードのみの実装で済むので、比較的コードがぐちゃぐちゃになりません。
それでも、上記の例のような書き方をすると、Container componentとPresentational componentが混在しており、やや見辛いコードとなっております。
また、検索フォームや新規登録フォームではどうでしょう？
たくさんのformの要素を使いますよね。

私自身、Formikで可読性や再利用生の高いコードをどう書こうかかなり悩みました。
特に、コンポーネント分割をする方法です。
分割したformのコンポーネントに、handleChangeやvaluesやnameをpropsとして渡せばうまく実装できそうな気がします。
ただ、全コンポーネントでhandleChangeやvaluesを渡すことに、まどろっこしさと抵抗を感じます。

Formikでこれらを**簡潔に**実装するためにはどうすれば良いでしょう。

検索フォームや新規登録フォームの実装は複雑になりがちなので、これ以降はお問い合わせフォームでの実装を考えます。
なお、以降のお問い合わせフォームは、私がFormikを解説するためだけの目的で作ったものです。
現実には存在しませんので、ご了承を。

## お問い合わせフォームの形式
以下のスクショのフォームを作成します。

*通常時*
<img width="469" alt="Screen Shot 2019-12-08 at 14.28.11.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/261546/83d09704-29e1-83c4-a688-ab31c6c41149.png">
*エラー時*
<img width="336" alt="Screen Shot 2019-12-08 at 16.07.06.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/261546/07e7ad76-fe56-9828-74d7-fd82cc17b34f.png">

実装はこんな感じですね。

```Contact.js
import React from "react";
import styled from "styled-components";
import { Formik } from "formik";
import * as Yup from "yup";

const Error = styled.span`
  color: red;
`;

const validation = () =>
  Yup.object().shape({
    trigger: Yup.string().required("必須項目です"),
    bothering: Yup.string().required("必須項目です"),
    wishing: Yup.string().required("必須項目です"),
    others: Yup.string().required("必須項目です")
  });


const Contact = () => {
  return (
     <Formik
      initialValues={{ trigger: "", bothering: "", wishing: "", others: "" }}
      validationSchema={validation()}
      onSubmit={values => console.log(values)}
      render={({ errors, handleChange, handleSubmit }) => (
        <form onSubmit={handleSubmit}>
          <div>
            <label>サービスを知ったきっかけは何か</label>
            <input name="trigger" onChange={handleChange} />
            <Error>{errors.bothering}</Error>
          </div>
          <div>
            <label>困っていること</label>
            <input name="bothering" onChange={handleChange} />
            <Error>{errors.trigger}</Error>
          </div>
          <div>
            <label>お願いしたいこと</label>
            <input name="wishing" onChange={handleChange} />
            <Error>{errors.wishing}</Error>
          </div>
          <div>
            <label>その他</label>
            <input name="others" onChange={handleChange} />
            <Error>{errors.others}</Error>
          </div>
          <button type="submit">送信</button>
        </form>
      )}
    />
  );
};

export default Contact;
```

上のコードを見て何か気づきますか？
name属性だけが違うinputタグが4つもあります。
早速、これらをDRYに書いていきましょう！

## コンポーネント分割
name属性とhandleChangeを受け取る**Input.js**を作りました。
再利用生を考え、Input.jsはerrorも受け取ってエラーメッセージを返すコンポーネントとしました。

```Input.js
import React from "react";
import styled from "styled-components";

const Error = styled.span`
  color: red;
`;

const Input = ({ name, error, handleChange }) => (
  <div>
    <input name={name} onChange={handleChange} />
    <Error>{error}</Error>
  </div>
);

export default Input;
```

このコンポーネントをimportして使ってみましょう！

```Contact.js
import React from "react";
import { Formik } from "formik";
import * as Yup from "yup";
import Input from "./Input";

const validation = () =>
  Yup.object().shape({
    trigger: Yup.string().required("必須項目です"),
    bothering: Yup.string().required("必須項目です"),
    wishing: Yup.string().required("必須項目です"),
    others: Yup.string().required("必須項目です")
  });

const Contact = () => {
  return (
     <Formik
      initialValues={{ trigger: "", bothering: "", wishing: "", others: "" }}
      validationSchema={validation()}
      onSubmit={values => console.log(values)}
      render={({ handleChange, handleSubmit }) => (
        <form onSubmit={handleSubmit}>
          <div>
            <label>サービスを知ったきっかけは何か</label>
            <Input
              name="trigger"
              error={errors.trigger}
              handleChange={handleChange}
            />
          </div>
          <div>
            <label>困っていること</label>
            <Input
              name="bothering"
              error={errors.bothering}
              handleChange={handleChange}
            />
          </div>
          <div>
            <label>お願いしたいこと</label>
            <Input
              name="wishing"
              error={errors.wishing}
              handleChange={handleChange}
            />
          </div>
          <div>
            <label>その他</label>
            <Input
              name="others"
              error={errors.others}
              handleChange={handleChange}
            />
          </div>
          <button type="submit">送信</button>
        </form>
      )}
    />
  );
};

export default Contact;
```

これだけでも、だいぶ変わりましたね！
4つのformの要素が同じようなデザインとなる場合、Input.jsを変更するだけでよくなりました！

しかし、propsでname, error, handleChangeの3つを送ることは、まだまだ冗長な記述になっているし、Formikからしたら無駄なことをしています。
もっと便利な方法を模索しましょう。

## Fieldコンポーネントを使う
どの要素にも必ず必要なhandleChangeをすべての要素でpropsとして渡しているのは、無駄なことだと思いませんか？
また、共通コンポーネントで各々のコンポーネントのerrorを出力する方法があったとしたら……？
FormikのFieldコンポーネントを使うと、handleChangeやerrorをpropsとして送る必要がなくなります。

```Input.js
import React from "react";
import styled from "styled-components";
import { Field } from "formik";

const Error = styled.span`
  color: red;
`;

const Input = ({ name }) => (
  <Field name={name}>
    {({ field, form: { errors } }) => (
      <div>
        <input {...field} />
        <Error>{errors[name]}</Error>
      </div>
    )}
  </Field>
);

export default Input;
```

```Contact.js
import React from "react";
import { Formik } from "formik";
import * as Yup from "yup";
import Input from "./Input";

const validation = () =>
  Yup.object().shape({
    trigger: Yup.string().required("必須項目です"),
    bothering: Yup.string().required("必須項目です"),
    wishing: Yup.string().required("必須項目です"),
    others: Yup.string().required("必須項目です")
  });

const Contact = () => {
  return (
    <Formik
      initialValues={{ trigger: "", bothering: "", wishing: "", others: "" }}
      validationSchema={validation()}
      onSubmit={values => console.log(values)}
      render={({ handleSubmit }) => (
        <form onSubmit={handleSubmit}>
          <div>
            <label>サービスを知ったきっかけは何か</label>
            <Input name="trigger" />
          </div>
          <div>
            <label>困っていること</label>
            <Input name="bothering" />
          </div>
          <div>
            <label>お願いしたいこと</label>
            <Input name="wishing" />
          </div>
          <div>
            <label>その他</label>
            <Input name="others" />
          </div>
          <button type="submit">送信</button>
        </form>
      )}
    />
  );
};

export default Contact;
```

必要なpropsは、nameだけです。
nameをInput.jsに渡せば、どの要素を変更するか、どの要素でエラーが発生しているかが、Formik側で検知してくるれます。

少しだけ解説をすると、**field** propsにはname, value, onChangeが、formにはvalues, errorsなどの値が入っています。
※デバッグしてみると面白いです。

## connectを使う
Fieldコンポーネントと同じことをconnectメソッドで行うことができます。
むしろ、こちらの方が直感的で簡潔な表現ができるのではないでしょうか。

```Input.js
import React from "react";
import styled from "styled-components";
import { connect } from "formik";

const Error = styled.span`
  color: red;
`;

const Input = ({ name, formik: { handleChange, errors } }) => {
  return (
    <div>
      <input name={name} onChange={handleChange} />
      <Error>{errors[name]}</Error>
    </div>
  );
};

export default connect(Input);
```

connectを使うことで、該当のコンポーネントにFormikで使用する変数やメソッドを、propsとして渡すことができます。

## 終わりに
このように、Formikには様々なことができるようにpropsやメソッド、コンポーネントが用意されています。
ここで紹介したものは、その一部でしかありません。

例えば、setFieldValueというメソッドを使うことで、条件に応じてvalueを変更することができ、より柔軟なformの実装をすることが可能となります。
同じようにsetFieldErrorでは、柔軟にエラーハンドリングを行うことができます。

私自身、まだまだFormikのドキュメントを読み込んでいないので、Formikの真価を発揮できているとは言い難いと思ってます。
今回解説した内容で、疑問に思う方、こういうやり方あるよーというのを知っている方、いやいやここ間違っとるやんと思う方、どしどしコメントお待ちしております！

また、POLでは私たちと一緒にサービスを作っていく仲間を募集しております。
弊社のフロントエンドは特に、まだまだ環境が整いきってません。
ぜひ、お力を貸して欲しいと思っております。
この記事を見てPOLに興味を持った方、アドバイスを持っている方、ぜひぜひお気軽にお声がけください！