ログイン画面にパスワード入力文字のチェック機能を導入してみた

# はじめに
　ログイン画面のパスワード入力フォームに文字数をチェックする機能を実装しました。後から見返すことができるように、そのやり方を記載します。

## 前提条件
- この記事ではバージョンが17.0.2のReactを使っています。
- Typescriptをインストールしています。
- アイコンの画像は[Material UI](https://mui.com/material-ui/material-icons/)から拾ってきました。
- スタイリングに関しての記述は省略します。
- ログイン認証をおこなう方法は、この記事では説明しておりません。

# 目次

1. [画面のレイアウトを作成する](#Chapter1)
1. [パスワードをチェックする関数を作成する](#Chapter2)
1. [パスワードの表示・非表示を切り替えられるようにする](#Chapter3)
1. [Firebase Authメソッドを使ってログインする](#Chapter4)
1. [参考文献](#reference)

<!-- 各チャプター -->
<a id="#Chapter1"></a>
# 画面のレイアウトを作成する

メールアドレスとパスワードの２つの入力フォームを備えたログイン画面を作成します。
パスワードはボタンのクリックに対応して、表示・非表示を切り替えることができるようにします。
``` React
import React, { useState } from "react";
// NOTE >> パスワードの表示・非表示をアイコンで示すため、Material UIからアイコンをインポートします
import { Visibility, VisibilityOff } from "@mui/icons-material";

const Login = () => {
  const [email, setEmail] = useState<string>("");
  // NOTE >> lengthCheck：パスワード入力フォームにおける文字数のチェック結果が記録されます。
  // NOTE >> patternCheck：パスワード入力フォームにおける文字種類のチェック結果が記録されます。
  const [password, setPassword] = useState<{
    lengthCheck: boolean;
    patternCheck: boolean;
    input: string;
  }>({
    lengthCheck: true,
    patternCheck: true,
    input: "",
  });
  const [showPassword, setShowPassword] = useState<boolean>(false);

  return (
    <div>
      <form>
        <div>
          <label>メールアドレス</label>
          <input
            type="email"
            value={email}
            onChange={(event: React.ChangeEvent<HTMLInputElement>) => {
              setEmail(event.target.value);
            }}
            required
          />
        </div>
        <div>
          <label>パスワード</label>
          <input
            type={showPassword ? "text" : "password"}
            onChange={(event: React.ChangeEvent<HTMLInputElement>) => {
              setPassword(checkPassword(event));
            }}
            required
          />
          <button onClick={() => {setShowPassword(!showPassword);}}>
            {showPassword ? (<Visibility />) : (<VisibilityOff />)}
          </button>
          {password.patternCheck === false && (
            <p>パスワードに使用できない文字が入力されています</p>
          )}
          {password.lengthCheck === false && (
            <p>パスワードの入力文字数は8文字以上20文字以下です</p>
          )}
        </div>
        <button disabled={!email || !password.input || !password.lengthCheck}>
          ログイン
        </button>
      </form>
    </div>
  );
};
export default Login;
```

### パスワードについて

ステート`password`には`lengthCheck`、`patternCheck`,`input`の３つのプロパティを持たせています。
　`lengthCheck`→入力された文字数のチェック結果を`boolean`で記録します
　`patternCheck`→入力された文字種類のチェック結果を`boolean`で記録します
　`input`→入力された文字を`string`で記録します。

### パスワードの表示・非表示について

ステート`showPassword`で`boolean`として管理します。
以下のとおり、アイコンボタンをクリックすることで表示・非表示を切り替えられるようにします。
アイコンボタンのアイコンは、`showPassword`の値に対応して、`<Visibility />`と`<VisibilityOff />`とで切り替えるようにします。
```react
  <button onClick={() => {setShowPassword(!showPassword);}}>
    {showPassword ? (<Visibility />) : (<VisibilityOff />)}
  </button>
```
 
<a id="#Chapter2"></a>
# パスワードをチェックする関数を作成する
パスワードをチェックする関数は以下のとおりです。
```React
import React from "react";

export const checkPassword = (event: React.ChangeEvent<HTMLInputElement>) => {
  const checkedResults: {
    lengthCheck: boolean;
    patternCheck: boolean;
    input: string;
  } = {
    lengthCheck: true,
    patternCheck: true,
    input: "",
  };
  // NOTE >> lengthCheckとpatternCheckの両方がtrueのとき、approvalをtrueにします
  const checkApproval = () => {
    if (checkedResults.lengthCheck && checkedResults.patternCheck) {
      checkedResults.input = event.target.value;
    } else {
      checkedResults.input = "";
    }
  };
  // NOTE >> 入力文字数をチェックします
  const length = event.target.value.length;
  if ((length > 0 && length < 8) || length > 20) {
    checkedResults.lengthCheck = false;
  } else {
    checkedResults.lengthCheck = true;
    checkApproval();
  }
  // NOTE >> 入力文字のパターンをチェックします
　　　　// NOTE >> regexはRegular Expressionの略です
  const regex = /^[a-z|A-Z|0-9|_]+$/;
  if (length === 0) {
    checkedResults.patternCheck = true;
  } else {
    checkedResults.patternCheck = regex.test(event.target.value);
    checkApproval();
  }
  return checkedResults;
};

```
正規表現だけでも、文字数が不足した場合や、許可されていない文字種類が入力された場合に`false`を返すことができますが、今回は以下のとおり、入力エラーの内容に応じて、JSXに警告文を表示したいため、あえてステートで管理するようにしています。

``` react
{password.patternCheck === false && (
  <p>パスワードに使用できない文字が入力されています</p>
)}
{password.lengthCheck === false && (
  <p>パスワードの入力文字数は8文字以上20文字以下です</p>
)}
```   

<a id="#reference"></a>
# 参考文献
- [パスワード(大文字や記号が少なくとも一文字含まれる)の正規表現](https://www.javadrive.jp/regex-basic/sample/index12.html)