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
orhandleSubmit
)
you callsetSubmitting(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の基礎的なことに触れましたが、深追いしていこうかなとも思います。
コメント