ログイン画面と登録画面の切り替え（フロントエンド）

# はじめに
Next.jsでログイン画面とアカウント登録画面を切り替えるUIを作ったので、メモとして残しておきます。
言語はTypescriptを使用しています。

# ポイント
**useCallback、useStateを使用する。**
今回だと、toggleVariantをspanタグのonClickで指定しているため、
このspanタグのテキストがクリックされたときに、画面を切り替えることが可能になる。

# 実装例
画像部分は好きなものに置き換えてください。（背景とロゴの部分２箇所）

```typescript:auth.tsx
import { useCallback, useState } from "react";
import Input from "../components/Input";

const Auth = () => {
  const [name, setName] = useState('')
  const [email, setEmail] = useState('')
  const [password, setPassword] = useState('')
  // ログイン画面、アカウント登録画面の切り替え
  const [variant, setVariant] = useState('login')
  
  // このtoggleVariantを、spanタグのonclickと紐づけている。
  const toggleVariant = useCallback(() => {
    setVariant((currentVariant) => currentVariant === 'login' ? 'register' : 'login')
  }, [])

  return (
    <div className="relative h-full w-full bg-[url('/images/任意の画像ファイル')] bg-no-repeat  bg-fixed bg-cover">
      <div className="bg-black w-full h-full lg:bg-opacity-50">
        <nav className="">
          <img src="/images/任意の画像ファイル" alt="Logo" className="h-24 w-auto" />
        </nav>
        <div className="flex justify-center">
          <div className="bg-black bg-opacity-90 px-14 py-14 self-center lg:w-2/5 lg:max-w-md rounded-md">
            <h2 className="text-white text-4xl mb-8 font-semibold">
              {/* 登録済みの場合、Sign In, 未登録の場合はRegister */}
              {variant === 'login' ? 'Sign In' : 'Register'}
            </h2>
            <div className="flex flex-col gap-4">
              {/* 氏名は登録時のみ入力 */}
              {variant === 'register' && (
                <Input
                  id="name"
                  onChange={(event:any) => setName(event.target.value)}
                  type="name"
                  label="Name"
                  value={name}
                />
              )}
              <Input
                id="email"
                onChange={(event:any) => setEmail(event.target.value)}
                type="email"
                label="Email"
                value={email}
              />
              
              <Input
                id="password"
                onChange={(event:any) => setPassword(event.target.value)}
                type="password"
                label="Password"
                // usestateのpassword
                value={password}
              />
              </div>
              <button className="bg-red-600 py-3 text-white rounded-md w-full mt-10 hover:bg-red-700 transition">
                {variant === 'login' ? 'Login' : 'Sign Up'}
              </button>
              <p className="text-neutral-500 mt-7">
                {variant === 'login' ? 'First time using Netflix?' : 'Already have an account?'}
                {/* このボタンを押したときに、アカウント作成、ログインの画面が切り替わるようにする */}
                <span onClick={toggleVariant} className="text-white ml-1 hover:underline cursor-pointer">
                  {variant === 'login' ? 'Create an account' : 'Sign In'}
                </span>
              </p>
          </div>
        </div>
      </div>
    </div>
  );
}

export default Auth;
```

auth.tsxで使用している、入力フォームのコンポーネント（Input）は以下のような実装
```typescript:input.tsx
interface InputProps {
  id: string;
  onChange: any;
  value: string;
  label: string;
  type?: string;
}

const Input: React.FC<InputProps> = ({
  id,
  onChange,
  value,
  label,
  type,
}) => {
  return (
    // inputタグとlabelタグの位置を揃えるには、親タグのclassNameにrelativeを指定する
    <div className="relative">
      <input
        id={id}
        type={type}
        value={value}
        onChange={onChange}
        className="
          block
          rounded-md
          px-6
          pt-7
          pb-3
          w-full
          text-md
          text-white
          bg-neutral-700
          appearance-none
          focus:outline-none
          focus:ring-0
          peer
        "
        // この空のplaceholderがないと、カーソルを合わせる前から文字が小さくなってしまう。
        placeholder=""
        />
      <label
      // 入力時はplaceholderの文字が上側に表示されるようにする
      className="
        absolute
        text-md
        text-zinc-400
        duration-150
        transform
        -translate-y-3
        scale-75
        top-5
        z-10
        origin-[0]
        left-5
        peer-placeholder-shown:scale-100
        peer-placeholder-shown:translate-y-0
        peer-focus:scale-75
        peer-focus:-translate-y-4
      "
      htmlFor={id}>
        {label}
      </label>
    </div>
  )
}
export default Input
```

# ブラウザ画面
背景画像、ロゴともDALL-E3で作ってもらいました。
クオリティ高くて素晴らしいです！！
https://openai.com/dall-e-3

**ログイン画面**

![スクショ（大）.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1850625/322ddf4a-23fc-6cd1-ed2d-36185e4de849.jpeg)

**アカウント登録画面（Create an accountをクリックすると切り替わる）**

![スクショ１（大）.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1850625/341cfd4c-eb45-aace-b0e4-3d8d6424eedd.jpeg)

# 終わりに
このようなログイン画面は作る機会多いと思うので、サクッと作れるようにしておきたいですよね。
また初めにNext.jsのプロジェクトを作成する際に、色々と聞かれるのですが、
```
✔ Would you like to use App Router? (recommended) … No
```
この問いをNoにしておかないと、pagesディレクトリが作成されず、appディレクトリが作成されてしまうので、気を付けてください。

こちらの記事のおかげで絶望せずに済みました。筆者の@mu_tomoyaさん、ありがとうございます！
参考：https://qiita.com/mu_tomoya/items/7545bea039e82e483f9e