LoginSignup
1
1

More than 1 year has passed since last update.

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

Last updated at Posted at 2021-11-29

はじめに

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


③ フォームの初期値を設定 (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)

setValuesを使えば、useStateの値を更新する時と同じ様な事が可能です。
今回の例では、投稿のテンプレートが欲しい時に「テンプレート」ボタンを押すと、入力フォームの内容がテンプレートの値に変更される処理です。


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

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でYupをインストール
$ npm install --save yup
// yarnでYupをインストール
$ yarn add yup



バリデーションの条件とエラーメッセージを記述

// 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は省略 --------
});


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

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)

バリデーションの処理は作成しましたが、このままではバリデーションに引っかかっても何のメッセージも出ない為、このままではバリデーションに引っかかっても何のメッセージも出ない為、ユーザーが困惑してしまいます。
そうならない為にエラーメッセージを出力する方法を紹介します。

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

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>

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



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

【バリデーションエラーメッセージが出力された時のイメージ】
Image.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を使用します。
resetForm()が動作すると、入力フォームの値がinitialValuesの値に戻ります。

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は省略 --------
});
1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1