# ログイン機能の作成1

次にログイン機能を作成します。

`feature/login-func`にて作業します。

```bash
git switch -c feature/login-func
```

まず、フォームの入力値をstateにて管理できるようにします。loginInfoというオブジェクトのステートを定義します。その際に、型宣言を別ファイルにて行いimportするようにします。

`src`配下に`types`ディレクトリを作成し、その中に`login.ts`を作成します。その中に`LoginInfoType`を宣言します。

<pre class="language-typescript" data-title="src/types/login.ts◎"><code class="lang-typescript"><strong>export type LoginInfoType = {
</strong><strong>  email: string
</strong><strong>  password: string
</strong><strong>}
</strong>
</code></pre>

そしてこの型を使用してstateを定義します。

<pre class="language-tsx" data-title="pages/LoginPage.tsx◎"><code class="lang-tsx"><strong>import { useState } from "react"
</strong>import { Input } from "../atoms/Input"
import { PrimaryBtn } from "../atoms/PrimaryBtn"
<strong>import { LoginInfoType } from "../../types/login"
</strong>
export const LoginPage = () => {
<strong>  const [loginInfo, setLoginInfo] = useState&#x3C;LoginInfoType>({
</strong><strong>    email: "",
</strong><strong>    password: "",
</strong><strong>  })
</strong>
  return (
    &#x3C;div className="w-[500px] bg-white rounded-lg shadow-lg py-10">
      &#x3C;form className="flex flex-col justify-center items-center gap-10">
        &#x3C;h1 className="text-3xl text-lime-800 font-bold text-center">
          ログイン
        &#x3C;/h1>
        &#x3C;div className="w-[80%]">
          &#x3C;Input
            type="text"
            placeholder="email"
          />
        &#x3C;/div>
        &#x3C;div className="w-[80%]">
          &#x3C;Input
            type="password"
            placeholder="password"
          />
        &#x3C;/div>
        &#x3C;PrimaryBtn onClick={() => null}>ログイン&#x3C;/PrimaryBtn>
      &#x3C;/form>
    &#x3C;/div>
  )
}
</code></pre>

このstateを変更する関数を作成しましょう。

<pre class="language-tsx" data-title="pages/LoginPage.tsx◎"><code class="lang-tsx"><strong>import { ChangeEvent, useState } from "react";
</strong>import { Input } from "../atoms/Input";
import { PrimaryBtn } from "../atoms/PrimaryBtn";
import { LoginInfoType } from "../../types/login";

export const LoginPage = () => {
  const [loginInfo, setLoginInfo] = useState&#x3C;LoginInfoType>({
    email: "",
    password: "",
  });

<strong>  const changeLoginInfo = (event: ChangeEvent&#x3C;HTMLInputElement>) => {
</strong><strong>    const { name, value } = event.target
</strong><strong>    setLoginInfo({ ...loginInfo, [name]: value })
</strong><strong>  }
</strong>
  return (
    &#x3C;div className="w-[500px] bg-white rounded-lg shadow-lg py-10">
      &#x3C;form className="flex flex-col justify-center items-center gap-10">
        &#x3C;h1 className="text-3xl text-lime-800 font-bold text-center">
          ログイン
        &#x3C;/h1>
        &#x3C;div className="w-[80%]">
          &#x3C;Input
            type="text"
            placeholder="email"
          />
        &#x3C;/div>
        &#x3C;div className="w-[80%]">
          &#x3C;Input
            type="password"
            placeholder="password"
          />
        &#x3C;/div>
        &#x3C;PrimaryBtn onClick={() => null}>ログイン&#x3C;/PrimaryBtn>
      &#x3C;/form>
    &#x3C;/div>
  );
};

</code></pre>

ではinputタグにてstateが変更できるように、`name`、`value`、`onChange`をセットします。

<pre class="language-tsx" data-title="pages/LoginPage.tsx◎"><code class="lang-tsx">...
  return (
    &#x3C;div className="w-[500px] bg-white rounded-lg shadow-lg py-10">
      &#x3C;form className="flex flex-col justify-center items-center gap-10">
        &#x3C;h1 className="text-3xl text-lime-800 font-bold text-center">
          ログイン
        &#x3C;/h1>
        &#x3C;div className="w-[80%]">
          &#x3C;Input
<strong>            name="email"
</strong>            type="text"
            placeholder="email"
<strong>            value={loginInfo.email}
</strong><strong>            onChange={changeLoginInfo}
</strong>          />
        &#x3C;/div>
        &#x3C;div className="w-[80%]">
          &#x3C;Input
<strong>            name="password"
</strong>            type="password"
            placeholder="password"
<strong>            value={loginInfo.password}
</strong><strong>            onChange={changeLoginInfo}
</strong>          />
        &#x3C;/div>
        &#x3C;PrimaryBtn onClick={() => null}>ログイン&#x3C;/PrimaryBtn>
      &#x3C;/form>
    &#x3C;/div>
  );
...
</code></pre>

次にログインボタンを押した際に、ログインするようにします。実際にはここでログイン用のAPIを叩きますが、今回はAPIを模した関数を作成します。

`src`配下に`api`ディレクトリを作成し、その中に`login.ts`を作成します。login.tsの中には以下のような関数を作成します。こちらの関数は、`loginInfo`のstateを受け取り、`email`が`test@example.com`で`、password`が`password`の場合に、`id`と`name`を返却し、それ以外はエラーとなるようにします。返却値に関しては、`types/login.ts`に宣言しましょう。

<pre class="language-typescript" data-title="types/login.ts◎"><code class="lang-typescript">export type LoginInfoType = {
  email: string
  password: string
}

<strong>export type LoginReturnType = {
</strong><strong>  id: number
</strong><strong>  name: string
</strong><strong>}
</strong></code></pre>

<pre class="language-typescript" data-title="src/api/login.ts◎"><code class="lang-typescript"><strong>import { LoginInfoType, LoginReturnType } from "../types/login"
</strong><strong>
</strong><strong>export const login = (info: LoginInfoType): LoginReturnType => {
</strong><strong>  const { email, password } = info
</strong><strong>  if (email === "test@example.com" &#x26;&#x26; password === "password") {
</strong><strong>    return { id: 1, name: "sample太郎" }
</strong><strong>  } else {
</strong><strong>    throw new Error()
</strong><strong>  }
</strong><strong>};
</strong></code></pre>

そして、ボタンを押した際の関数を作成します。今回formタグのonSubmitにセットしますので、デフォルトの挙動をキャンセルするため、`event.preventDefault()`を実行します。そして先ほどの`login`関数を呼び出します。この際に、`email`もしくは`password`に誤りがある場合は例外が発生し、`catch`文に入ります。エラー時は、`errorMessage`というstateにて管理し、errorMessageに文字が入っている場合は画面に表示するようにしましょう。

<pre class="language-tsx" data-title="pages/LoginPage.tsx◎"><code class="lang-tsx"><strong>import { ChangeEvent, FormEvent, useState } from "react";
</strong>import { Input } from "../atoms/Input";
import { PrimaryBtn } from "../atoms/PrimaryBtn";
import { LoginInfoType } from "../../types/login";
<strong>import { login } from "../../api/login";
</strong>
export const LoginPage = () => {
  const [loginInfo, setLoginInfo] = useState&#x3C;LoginInfoType>({
    email: "",
    password: "",
  })
<strong>  const [errorMessage, setErrorMessage] = useState("");
</strong>
  const changeLoginInfo = (event: ChangeEvent&#x3C;HTMLInputElement>) => {
    const { name, value } = event.target
    setLoginInfo({ ...loginInfo, [name]: value })
  }

<strong>  const handleLogin = (event: FormEvent&#x3C;HTMLFormElement>) => {
</strong><strong>    event.preventDefault()
</strong><strong>    setErrorMessage("")
</strong><strong>    try {
</strong><strong>      login(loginInfo)
</strong><strong>    } catch {
</strong><strong>      setErrorMessage("ログインに失敗しました");
</strong><strong>    }
</strong><strong>  };
</strong>
  return (
    &#x3C;div className="w-[500px] bg-white rounded-lg shadow-lg py-10">
      &#x3C;form className="flex flex-col justify-center items-center gap-10" onSubmit={handleLogin}>
        &#x3C;h1 className="text-3xl text-lime-800 font-bold text-center">
          ログイン
        &#x3C;/h1>
<strong>        {errorMessage !== "" &#x26;&#x26; (
</strong><strong>          &#x3C;div className="p-5 bg-red-500 text-white w-[80%] rounded-lg">
</strong><strong>            {errorMessage}
</strong><strong>          &#x3C;/div>
</strong><strong>        )}
</strong>        &#x3C;div className="w-[80%]">
          &#x3C;Input
            name="email"
            type="text"
            placeholder="email"
            value={loginInfo.email}
            onChange={changeLoginInfo}
          />
        &#x3C;/div>
        &#x3C;div className="w-[80%]">
          &#x3C;Input
            name="password"
            type="password"
            placeholder="password"
            value={loginInfo.password}
            onChange={changeLoginInfo}
          />
        &#x3C;/div>
        &#x3C;PrimaryBtn onClick={() => null}>ログイン&#x3C;/PrimaryBtn>
      &#x3C;/form>
    &#x3C;/div>
  );
};

</code></pre>

これで`test@example.com`と`password`と入力することで、ログインが成功し、それ以外はエラーメッセージが表示されるかと思います。

<figure><img src="https://1869761657-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FcUBbYqol4PMzZJggiMqV%2Fuploads%2FieNdwhZz8TNxTa7n53wM%2F3a.png?alt=media&#x26;token=032df2c0-d474-429b-9dd2-c4106f72b5b1" alt=""><figcaption></figcaption></figure>

ここまでの作業をcommitしましょう。

```bash
git add .
```

```bash
git commit
```

commitメッセージは

```
feat: ログインフォームをstateにて管理できるようにし、ログイン失敗時のエラーハンドリングを追加
```

としましょう。
