GitHub Copilotで、ReactのuseStateを完全に理解した

# はじめに

https://qiita.com/Jasmine_teeeeee/items/d087b0b309508c7ceaec

これの続きです。

# STEP
・[要件整理（画面デザイン作成）](https://qiita.com/Jasmine_teeeeee/items/e65e6e41f1eaced9d71d)　
・フロンドエンド開発
　・[静的画面](https://qiita.com/Jasmine_teeeeee/items/d087b0b309508c7ceaec)
　・[表示切り替え（useState）](https://qiita.com/Jasmine_teeeeee/items/71d5caa3f1d54ccf8918)　 ←いまここ
　・[画面遷移（useRouter）](https://qiita.com/Jasmine_teeeeee/items/6848f94dcec817d3e04f) 
　・[APIモック作成](https://qiita.com/Jasmine_teeeeee/items/76294bb468a065400b0e)
・バックエンド開発

## 今回のやることとゴール

このSTEPではひとつ前の工程作成したログイン画面に
ログイン画面とアカウント登録画面の切り替え（useState）処理を実装してみたいと思います。

## 目次

１\. 表示切替処理を追加
２\. テストレコードの作成
３\. 子コンポーネントにuseStateを渡す

# 開発開始

## ログイン画面とアカウント登録画面の切り替え（useState）

### 完成画面イメージ

ログイン画面はこちら

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3778585/71791801-8b67-c5d9-09aa-b5ad9ac357d7.png)

「新規作成はこちら」をクリックした際の画面はこちら
システムタイトルは固定で入力テキストボックスのエリアが切り替わります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3778585/7b7e2282-6b50-70d0-f2e1-ef41c035d0ee.png)

### ルートページのコード

```page.tsx
'use client'

import LoginForm from './login/page'

export default function Home() {

  return (
    <div className="min-h-screen bg-gray-100 flex items-center justify-center p-4 font-noto-sans">
      <div className="bg-white rounded-lg shadow-lg p-8 w-full max-w-md">
        <h1 className="text-2xl font-bold text-center mb-8 text-gray-800">
          連絡帳システム
        </h1>
        <LoginForm />
      </div>
    </div>
  )
}
```

### 表示切替処理を追加

ルートページファイルをCopilotの編集に追加

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3778585/8b57eaca-67f8-32df-aa71-811ab14d9284.png)

以下呪文を唱える
>React の useState フックを使用して、ビュー（画面表示）の状態を管理してください。 
>
>ログイン状態の場合、
>LoginFormコンポーネントを表示し、
>
>アカウント登録状態の場合、
>LoginFormコンポーネントを非表示にして、固定文字「アカウント登録ページは準備中>です」を表示したいです。
またその文字の下にはログイン状態に戻す用のリンクを表示してください。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3778585/34583092-24f8-6129-84b5-57625d6edda8.png)

#### 動作確認

テスト用のボタンを追加して動作確認します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3778585/4e864ab9-cbbc-75e8-2094-8a885a469298.png)

Toggle to Loginリンクをクリック

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3778585/6089940c-bd39-65fb-1d61-6d02a6dfc27b.png)

ちゃんと表示が切り替わりました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3778585/5759721f-d8cc-6184-1481-1d2cdce89ca2.png)

ログインページに戻るボタンも動作しました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3778585/6089940c-bd39-65fb-1d61-6d02a6dfc27b.png)

### テストレコードの作成

:::note warn
コードは生成できましたが、うまくファイルを新規作成できませんでした。
手動でファイルを保存してください。
またエラーが出た場合は、Copilotに質問しながら解消します。
:::

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3778585/caed5973-ba2e-9d6f-7617-cee1f295682b.png)

自動生成されたコードは以下

:::note warn
今回自動生成したファイルには、モック化とimportしたコンポーネントの辻褄が合わない箇所がありますが、
必要に応じて修正してください。
:::

```page.test.tsx

import { render, screen, fireEvent } from '@testing-library/react'
import '@testing-library/jest-dom'
import Home from './page'
import LoginForm from './login/page'

jest.mock('./login/page', () => {
    return jest.fn(() => <div data-testid="mock-login-form">Login Form</div>)
})

describe('Home component', () => {
    it('renders heading text', () => {
        render(<Home />)
        expect(screen.getByText('連絡帳システム')).toBeInTheDocument()
    })

    it('shows login form by default', () => {
        render(<Home />)
        expect(screen.getByTestId('mock-login-form')).toBeInTheDocument()
    })

    it('toggles between login and register views', () => {
        render(<Home />)
        
        // Click toggle button to show register view
        fireEvent.click(screen.getByText('Toggle to Login'))
        expect(screen.getByText('アカウント登録ページは準備中です')).toBeInTheDocument()
        
        // Click back to login button
        fireEvent.click(screen.getByText('ログインページに戻る'))
        expect(screen.getByTestId('mock-login-form')).toBeInTheDocument()
    })
})

```

テストレコードの意味を解説してもらいましょう。

ファイル内のコードを全選択するか、
@workspace /explain　で対象ファイルを設定します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3778585/dd2cc0d9-101f-65ac-2819-e71907d9ec1f.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3778585/95a59319-de37-e7a4-9bbe-39a69397e73b.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3778585/2642be3c-4e38-cb1a-65b3-33e54c7bacbe.png)

実行してみます

>npx jest src/app/page.test.tsx

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3778585/1c1d0194-11b4-5280-27c3-e45a699115f9.png)

エラーになったので、Copilotに質問します。

修正方法を教えてくれたので、実行します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3778585/de3415ab-9037-515b-2110-96368398e941.png)

通るようになりました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3778585/e47e563b-9d88-0fdc-f5b4-3929be318f7e.png)

### 子コンポーネントにuseStateを渡す

テスト用の「Toggle to Login」を削除し、
「新規登録はこちら」リンクから画面の切り替えができるようにします。

子コンポーネントを選択して、Ctrl＋I　で追加します。
（この依頼ができるなら手で追加した方が速そうだけどｗ）

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3778585/ddb0d25e-e1d6-be2d-e8ad-daae574cc911.png)

子コンポーネント側を修正します。

:::note warn
エラー箇所から追加できないか色々試しましたが、今時点その修正方法はできないようでした。
明示的にpropsの追加を依頼してください。
:::

追加した引数を追加してもらいます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3778585/ba911338-9663-24c9-4178-1cb7003d11fe.png)

修正されました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3778585/174cfd20-9fb8-ad80-4dd0-76b58ec9bc95.png)

※Dispatchはわからない。この方の記事を読んでみなさんは理解してください。

https://qiita.com/blkclct/items/15f0c06f3a7d9fe142af

親コンポーネントで引数を追加した際に、対象リンクを指定したので、ボタンクリック処理も修正されていました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3778585/cd693318-6947-c031-31a3-49f51a034fa8.png)

#### 動作確認

キャプチャは省略しますが、
「新規登録はこちら」リンクをクリックして、ちゃんと表示が切り替わりました。
ログインページに戻るボタンも動作しました。

#### テスト再実行

TODO

めっちゃ書き換わっちゃったので、追っていい感じになったら記事更新します。

# まとめ

たたき台をAIに作ってもらって、そのコードの意味を質問したり調べたりして、
妥当なコードを採用し、意味を理解しながら進めるとよいと思います。