Reactの状態管理(global state)Context API の使い方 

# Context APIとは
通常のpropsだと親から子から孫と渡さないといけないところをContextを使うと、どこからでも直接使えるようになります。

今回はログイン中のユーザーで説明していきます。***全コンポーネントで使い回すための仕組み***です。

ex)
- ヘッダーで「◯◯さん、こんにちは」と表示
- ログアウトボタンで setLoginUser({ id: 0, name: "",email:""}) として初期化
- API に loginUser.id を渡してリクエストを送る

# 使い方(ログイン中のユーザーを使い回す想定)
ここで説明するのは、***ログイン中のユーザー情報（id, name,email）をアプリ全体で使いまわす***」ための ```Context API```の実装です。

### 1.型定義
```type/login.ts
export type LoginUserType = {
  id: number;
  name: string;
  email: string;
};
```

### 2.Context作成
```contexts/LoginUserContext.tsx
import { createContext, useState, ReactNode } from "react";
import { LoginUserType } from "../types/login";

export type LoginUserContextType = {
  loginUser: LoginUserType
  setLoginUser: (user: LoginUserType) => void;
};

export const LoginUserContext = createContext<LoginUserContextType | undefined>(undefined);

export const LoginUserProvider = ({ children }: { children: ReactNode }) => {
  const [loginUser, setLoginUser] = useState<LoginUserType>({
    id: 0,
    name: "",
    email: "",
  });

  return (
    <LoginUserContext.Provider value={{ loginUser, setLoginUser }}>
      {children}
    </LoginUserContext.Provider>
  );
};
```



### 3.アプリ全体をProviderで囲む

```main.tsx
import React from 'react'
import ReactDOM from 'react-dom/client'
import { RouterProvider } from 'react-router-dom'
//省略
import { router } from './routes'
import { LoginUserProvider } from "./contexts/LoginUserContext";

ReactDOM.createRoot(document.getElementById('root')!).render(
  <React.StrictMode>
    <LoginUserProvider>
      <RouterProvider router={router}/>
    </LoginUserProvider>
  </React.StrictMode>
)
```

### 4. どこでも使えるようにする（useContext）
```Header.tsx
import { useContext } from "react";
import { LoginUserContext } from "../contexts/LoginUserContext";

export const Header = () => {
  const context = useContext(LoginUserContext);
  if (!context) throw new Error("LoginUserContextが設定されていません");

  const { loginUser } = context;

  return <div>{loginUser.name} さんは（{loginUser.email}）でログイン中</div>;
};
```

### 5.コンテキストを呼び出すファイル(カスタムフック)作成
```hooks/useLoginUser.tsx
import { useContext } from 'react'
import { LoginUserContext } from '../contexts/LoginUserContext';

export const useLoginUser = () => {
//LoginUserContext から現在の値を取得。これが undefined の場合＝Providerで囲まれていない ということ
  const context = useContext(LoginUserContext);
//エラーを明示的に投げて、開発者に「Providerで囲んでないよ！」と気づかせる。これがあることで安全なコードになる
  if(context === undefined) {
    throw new Error('useLoginUser must be used within a LoginUserProvider');
  }
  return context; //context（つまり { loginUser, setLoginUser }）を返す。呼び出し元でこれを使えば、ログインユーザー情報に簡単にアクセス可能
}
```

### 6. どこでも使えるようにする（useContext）
例：Header コンポーネントなどで

```components/Header.tsx
import { useLoginUser } from "../hooks/useLoginUser";

const Header = () => {
  const { loginUser } = useLoginUser();

  return (
    <header className="p-4 bg-gray-100">
      <p>{loginUser.name} さん（{loginUser.email}）でログイン中</p>
    </header>
  );
};

export default Header;
```

# その他状態管理
#### Redux
学習コストが高い😅

#### Zustand
軽量な使いやすい状態管理ライブラリでReduxの代替になります。

#### Recoil
Recoilの開発は終了しました。

https://zenn.dev/humanhacker/articles/88685dfa915474?redirected=1

# 状態管理を最適化したい
ContextAPIなどを使っていると一見良さそうですが、パフォーマスが下がってしまったりします。コードが肥大化してしまい処理が複雑になってしまったりします。
そこでグローバル管理を最低限にするのがいいです。
必要なのが```TanStackQuery```(旧React Query)です。

https://zenn.dev/taisei_13046/books/133e9995b6aadf/viewer/573083


# 参考資料
- ChatGPT

- HapinessChainの無料特典

[ゆうだい@プログラミングで月100万](https://www.youtube.com/@yudai-hc)