前回はTypescript×React×Hooks 及び Firebase Authentication を用いて作成している会員管理アプリに、Contextを活用した状態管理を導入し、アプリの規模拡大に対する耐性を強化しました。今回はFormikとYupを活用してフォームバリデーションを追加していきます。
全 3 回の内容は下記です。
- Firebase Auth で認証基盤外出し
- Context でアプリの状態管理
- Formik と Yup でフォームバリデーション
利用している技術要素
- Firebase Authentication
- Typescript
- React
- React Hooks
- Context
- Material UI
- Formik ←NEW
- Yup ←NEW
ソースコード
前回との差分 も一応貼っておきます。
デモ
フォームバリデーションにより下記のような機能が追加されています。
- 必須項目のバリデーション
- 最小文字数のバリデーション
- バリデーションを通った(Valid)場合Submitボタンをクリックできるようになる
React アプリのポイント解説
前回との差分中心に説明します。
いくつかライブラリが追加されていますが、これまで同様ソースコードからそのままyarn start
すれば動くと思います。
会員登録用のSignup.tsx
とログイン用のLogin.tsx
それぞれほとんど同じような変更を加えているので、Signup.tsx
を中心に見ていきます。
import { Field, Form, Formik } from "formik";
// Material UIのために、formikのFormコンポーネントに渡す必要がある。 @material_ui/core にも同名のものがあり注意
import { TextField } from "formik-material-ui";
import React, { Fragment, useContext, useEffect } from "react";
import * as Yup from "yup";
import {
Button,
Container,
FormControl,
Grid,
Link,
Typography,
LinearProgress
} from "@material-ui/core";
import { AuthContext } from "../Auth";
import auth from "../firebase";
// Yupでバリデーション用のスキーマを定義。Login.tsxでも利用するのでexportしている
export const AuthSchema = Yup.object().shape({
email: Yup.string()
.email()
.required(),
password: Yup.string()
.min(6)
.required()
});
const Signup = (props: any) => {
const { currentUser } = useContext(AuthContext);
useEffect(() => {
currentUser && props.history.push("/");
}, [currentUser]);
return (
<Fragment>
<Container>
<Grid container>
<Grid item md={4}></Grid>
<Grid item md={4}>
<Formik
// フォームの初期値を定義
initialValues={{ email: "", password: "" }}
// Yupで定義したスキーマを利用
validationSchema={AuthSchema}
// フォームSubmit時のイベントを定義。Firebase APIを呼んでいる
onSubmit={async value => {
try {
await auth.createUserWithEmailAndPassword(
value.email,
value.password
);
props.history.push("/login");
} catch (error) {
alert(error.message);
}
}}
// フォームの描画を定義。今回はFormikで持つオブジェクトを3つ渡してフォーム内で利用している
// submitForm:上記で定義したフォームSubmit時のイベント
// isSubmitting:Submit中かどうかの状態判定
// isValid:フォームがValidでSubmit可能化の状態判定
render={({ submitForm, isSubmitting, isValid }) => (
<Form>
// Submit中にプログレスバーを表示
{isSubmitting && <LinearProgress />}
<FormControl margin="normal" fullWidth>
<Field
style={{ marginTop: "0.5em", marginBottom: "0.5em" }}
name="email"
label="E-mail"
fullWidth
variant="outlined"
component={TextField}
/>
</FormControl>
<FormControl fullWidth>
<Field
style={{ marginTop: "0.5em", marginBottom: "0.5em" }}
name="password"
label="Password"
fullWidth
variant="outlined"
type="password"
component={TextField}
/>
</FormControl>
<FormControl fullWidth>
<Button
fullWidth
// Submit時のイベントを紐付け
onClick={submitForm}
style={{ marginTop: "0.5em", marginBottom: "0.5em" }}
type="submit"
// フォームがValidでない or Submit中の場合、SubmitボタンをDisabledにする
disabled={!isValid || isSubmitting}
>
Sign up
</Button>
<Typography align="center">
<Link href="/login">to login</Link>
</Typography>
</FormControl>
</Form>
)}
/>
</Grid>
<Grid item md={4}></Grid>
</Grid>
</Container>
</Fragment>
);
};
export default Signup;
コード内にコメントを書いたので、読めば何をしているかわかるかと思います。
Material UIとFormikをあわせて利用している例があまりなかったので、動かすまで苦労しました。特にフォームの各フィールドにはformikが提供するFormを利用し、component属性にformik-material-uiが提供するTextFieldを渡してあげる部分がわかりにくかったのを記憶しています。
最後に
3回にわけて、Typescript×React×Firebase Authenticationで会員管理ができるアプリを作り、その上に機能追加をしていけるような骨組みを構築しました。
より本格的なアプリケーションにするためには、例えば下記要素なんかが考慮されると思います。
- APIコール
- 複数言語対応
- ヘッダーやフッターといったパーツの設計
- ロギング
- テスト
- ...
こう考えるとやはりアプリケーション開発は奥が深いですね。日々精進です。