React.js useFormikの使い方を入門者向けに、詳しく詳しく解説！！

# はじめに
ReactでFormikを使う方法を丁寧に説明します。
各属性(initialValues, handleChange等々)の説明がざっくりしている記事ばかりで、よくわからない！！って方向けにじっくり説明してます。
React初心者でもあるので間違ってたら、逆にガンガン教えてください！

# 目次
<!-- タイトルとアンカー名を編集 -->
1. [初期の入力フォーム]
2. [formikをインストール]
3. [initialValues, handleSubmit]
4. [handleChange]
5. [setValues]
6. [validationSchema, Yup]
7. [errors, touched]
8. [API]
9. [resetForm]
10. [最後に]

<!-- 各チャプター -->
# ① 初期の入力フォーム
このショボい入力フォームでuseFormikを使用していきます。
chakra-uiというUIコンポーネントを使用していますが、HTMLのlabelやinputタグと大して変わらないので気にしないでください。

```
return (
  <>
    <div>
      <FormLabel>
        <p>タイトル</p>
      </FormLabel>
      <Input　type="text"　/>
    <div>

    -------- contentとtagの入力フォームは省略 --------

    <Button>送信</Button>
  </>
);
```

# ② formikをインストール

まずはformikをインストールします。

```
// npmでインストール
$ npm install formik --save
```

```
// yarnでインストール
$ yarn add formik
```
<br>

# ③ フォームの初期値を設定 (initialValues) 及び、送信ボタンを押した際の動作 (handleSubmit)
・useFormikのinitialValues属性に初期値を記述すると、各入力フォームの初期値が設定できます。
・フォームの送信ボタンを押すと、onFinishメソッドが発火→handleSubmitのonSubmit内の処理が動作します。

```
// useFormikをインポート
import { useFormik } from 'formik';

--------　途中は省略　--------

// 入力フォームの初期値 (initialValues属性にセットする)
const initialPostValue = {
 title: '',
 content: '',
 tag: '',
};

// 下記追記
const {
    // ここにフォームで入力した値や初期値が入る
    values: newPostData,

　　　　　　　　// この場合、onFinishメソッドを叩くと、onSubmit内の処理が走る
    // フォームのデータ送信のアクションに用いられる　
    handleSubmit: onFinish,

  // TypeScriptなので、newPostDataの型定義をここで行なっている
  } = useFormik<PostData>({

    // 初期値を設定
    initialValues: initialPostValue,

　　　　　　　　// handleSubmit属性のonFinishが発火　→ onSubmit内の処理が走る
    onSubmit: async () => {
      alert('フォーム送信成功！！');
    },
  });


return (
  <>
    <div>
      <FormLabel>
        <p>タイトル</p>
      </FormLabel>
      <Input　
        type="text"
        // nameにtitleを追記
        name="title"
　　　　　　　　　　　　　　　　// newPostData.titleを追記　→ initialPostValueで設定したtitleの初期値が入る
        value={newPostData.title}
      />
    <div>

    -------- contentとtagの入力フォームは省略 --------

    // onClickでonFinish()を追記
    <Button onClick={() => onFinish()}>送信</Button>
  </>
);
```



# ④ フォームの入力データを取得 (handleChange)
handleChangeを以下の様に追記するだけで、ユーザーが入力したデータがnewPostDataに入ってきます。
これでユーザーが入力したデータを簡単に取得出来ます。
```console.log(newPostData);```で確認すると、入力データが取得出来ている事がわかります。


```

--------　以上省略　--------

const {
    values: newPostData,
    handleSubmit: onFinish,
    // 下記追記
    handleChange,
  } = useFormik<PostData>({
    initialValues: initialPostValue,
    onSubmit: async () => {
      alert('フォーム送信成功！！');
    },
  });

return (
  <>
    <div>
      <FormLabel>
        <p>タイトル</p>
      </FormLabel>
      <Input
        type="text"
        name="title"
        value={newPostData.title}
        // これを全ての入力フォームに追記するだけで、newPostDataに入力データが入ってくる
        onChange={handleChange}
      />
    <div>

    -------- 以下省略 --------
  </>
);
```

# ⑤ フォームの入力データを更新 (setValues)
<font color="blue">__setValuesを使えば、useStateの値を更新する時と同じ様な事が可能です。__</font>
今回の例では、投稿のテンプレートが欲しい時に「テンプレート」ボタンを押すと、入力フォームの内容がテンプレートの値に変更される処理です。

```

--------　以上省略　--------

const {
    values: newPostData,
    // 下記追記
    setValues: setNewPostData,
    handleSubmit: onFinish,
    handleChange,
  } = useFormik<PostData>({
    initialValues: initialPostValue,
    onSubmit: async () => {
      alert('フォーム送信成功！！');
    },
  });

// テンプレートボタンをクリックすると、入力フォームにテンプレートの値が入力される。
const onClickMakeTemplate = () => {
  // useStateの値を更新する時と同じ様な事が出来ます
  setNewPostData({ 
    title: 'Formikの使い方',
    content: 'formikをインストールします。その後・・・',
    tag: 'React',
  });
};

return (
  <>
    -------- 途中の入力フォームは省略 --------
    
    // テンプレート作成ボタンを追記
    <Button onClick={() => onClickMakeTemplate()}>テンプレート</Button>

    <Button onClick={() => onFinish()}>送信</Button>
  </>
);
```

# ⑥ フォームのバリデーションを設定 (validationSchema, Yup)
今回はFormikとよく組み合わせて使用される、Yupというライブラリでバリデーションを行います。
まずは、Yupをインストールし、どこかにバリデーション用のファイルを作成して、それをimportしてバリデーションを使用します。
ファイル作成後は下記の様に記述してください。

```
// npmでYuｐをインストール
$ npm install --save yup
```

```
// yarnでYuｐをインストール
$ yarn add yup
```

<br>
<font color="blue">__バリデーションの条件とエラーメッセージを記述__</font>

```
// src/utils/validation/schema.tsファイル

import * as Yup from 'yup';

export const PostSchema = Yup.object().shape({
  // titleのフォームのバリデーション
  // 型はstringで入力
  title: Yup.string()
  　　　　// required()で入力必須にする
    .required('タイトルは入力必須です')
  　　　　// max()で文字数制限を設ける
    .max(50, 'タイトルは50文字以内で入力してください'),
  --------　contentとtagは省略 --------
});
```
<br>

Yupのバリデーションファイルを作成後、importして使用します。
これでフォームの送信ボタンを押しても、<font color="blue">__入力内容に不備があればバリデーションに引っかかり、onSubmitの処理が走るのを防げます。__</font>

```
import { useFormik } from 'formik';
// 下記追記
import { PostSchema } from 'src/utils/validation/schema';

--------　途中は省略　--------

const {
    values: newPostData,
    setValues: setNewPostData,
    handleSubmit: onFinish,
    handleChange,
  } = useFormik<PostData>({
    initialValues: initialPostValue,
    // バリデーションの条件として、schemaファイルのPostSchemaを使用する
    validationSchema: PostSchema,
    onSubmit: async () => {
      alert('フォーム送信成功！！');
    },
  });
```

# ⑦ バリデーションエラーメッセージを表示する (errors, touched)
バリデーションの処理は作成しましたが、<font color="blue">__このままではバリデーションに引っかかっても何のメッセージも出ない__</font>為、このままではバリデーションに引っかかっても何のメッセージも出ない為、ユーザーが困惑してしまいます。
そうならない為にエラーメッセージを出力する方法を紹介します。

```
--------　以上省略　--------

const {
    values: newPostData,
    setValues: setNewPostData,
    handleSubmit: onFinish,
    handleChange,
    // 下記追記
    errors,
    // 下記追記
    touched,
  } = useFormik<PostData>({
    initialValues: initialPostValue,
    validationSchema: PostSchema,
    onSubmit: async () => {
      alert('フォーム送信成功！！');
    },
  });

return (
  <>
    <div>
      <FormLabel>
        <p>タイトル</p>
        //　更新ボタンを押してバリデーションエラー　& 入力フォームをクリック　→ エラーメッセージを出力
        {errors.title && touched.title ? (
          // タイトルを空で送信した場合、'タイトルは入力必須です'を出力
          <p>{errors.title}</p>
        ) : null}
      </FormLabel>
      <Input
        type="text"
        name="title"
        value={newPostData.title}
        onChange={handleChange}
      />
    <div>

    -------- 以下省略 --------
  </>
);
```

<br>
・タイトルを空にして、送信ボタンを押した際、```console.log(errors.title);```を確認すると、'タイトルは入力必須です'と出力されます。
・touched.titleは<font color="blue">__タイトルの入力フォームに触れた時点で、trueを出力__</font>します。

【バリデーションエラーメッセージが出力された時のイメージ】
![Image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1416941/c6131f9e-ee67-191e-381f-915cef3f94a4.png)


# ⑧ APIの処理
色々はすっ飛ばしてますが、とりあえず入力フォームのデータをDBに登録出来ました。
入力したデータをDBに送信しましょう。

```
// これで、APIの処理が無事成功すれば、投稿完了
const { mutate: createNewPost } = useAPICreatePost({
  onSuccess: async () => {
   alert('投稿が完了しました。');
  },
});

const {
    values: newPostData,
    setValues: setNewPostData,
    handleSubmit: onFinish,
    handleChange,
    errors,
    touched,
  } = useFormik<PostData>({
    initialValues: initialPostValue,
    validationSchema: PostSchema,
    onSubmit: async () => {
      // 投稿作成のAPIの処理を追記し、入力フォームの値をDBに送信する
      createNewPost(newPostData);
    },
  });
```

# ⑨ フォームのリセット (resetForm)
DBへの登録が完了したものの、フォームには前回の値がそのまま残っています。もし、DBに登録が完了した際にフォームの内容をリセットしたい場合はresetFormを使用します。
<font color="blue">__resetForm()が動作すると、入力フォームの値がinitialValuesの値に戻ります。__</font>

```
const { mutate: createNewPost } = useAPICreatePost({
  onSuccess: async () => {
   alert('投稿が完了しました。');
   // 投稿が完了すると、フォームの入力内容を初期値に戻す (initialValuesの値)
   resetForm()
  },
});

const initialPostValue = {
 title: '',
 content: '',
 tag: '',
};

const {
    values: newPostData,
    setValues: setNewPostData,
    handleSubmit: onFinish,
    handleChange,
    errors,
    touched,
    // 下記追記
    resetForm,
  } = useFormik<PostData>({
    initialValues: initialPostValue,
    validationSchema: PostSchema,
    onSubmit: async () => {
      createNewPost(newPostData);
    },
  });
```



# ⑩ 最後に
色々省略したりハショッってますが、formikの部分はちゃんと書きました。
とりあえず最後にはこんな感じになります。
なんか間違ってたらガンガン意見ください！！
以上です！！

```
import { useFormik } from 'formik';
import { PostSchema } from 'src/utils/validation/schema';

--------　途中は省略　--------

const { mutate: createNewPost } = useAPICreatePost({
  onSuccess: async () => {
   alert('投稿が完了しました。');
   resetForm()
  },
});

const initialPostValue = {
 title: '',
 content: '',
 tag: '',
};

const {
    values: newPostData,
    setValues: setNewPostData,
    handleSubmit: onFinish,
    handleChange,
    errors,
    touched,
    resetForm,
  } = useFormik<PostData>({
    initialValues: initialPostValue,
    validationSchema: PostSchema,
    onSubmit: async () => {
      createNewPost(newPostData);
    },
  });

const onClickMakeTemplate = () => {
  setNewPostData({ 
    title: 'Formikの使い方',
    content: 'formikをインストールします。その後・・・',
    tag: 'React',
  });
};

return (
  <>
    <div>
      <FormLabel>
        <p>タイトル</p>
        {errors.title && touched.title ? (
          <p>{errors.title}</p>
        ) : null}
      </FormLabel>
      <Input
        type="text"
        name="title"
        value={newPostData.title}
        onChange={handleChange}
      />
    <div>

    -------- contentとtagの入力フォームは省略 --------

    <Button onClick={() => onClickMakeTemplate()}>テンプレート</Button>

    <Button onClick={() => onFinish()}>送信</Button>
  </>
);
```

```
// src/utils/validation/schema.tsファイル

import * as Yup from 'yup';

export const PostSchema = Yup.object().shape({
  title: Yup.string()
    .required('タイトルは入力必須です')
    .max(50, 'タイトルは50文字以内で入力してください'),
  --------　contentとtagは省略 --------
});
```