はじめに
ReactでFormikを使う方法を丁寧に説明します。
各属性(initialValues, handleChange等々)の説明がざっくりしている記事ばかりで、よくわからない!!って方向けにじっくり説明してます。
React初心者でもあるので間違ってたら、逆にガンガン教えてください!
目次
- [初期の入力フォーム]
- [formikをインストール]
- [initialValues, handleSubmit]
- [handleChange]
- [setValues]
- [validationSchema, Yup]
- [errors, touched]
- [API]
- [resetForm]
- [最後に]
① 初期の入力フォーム
このショボい入力フォームで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を出力__します。
⑧ 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は省略 --------
});