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の開発は終了しました。
状態管理を最適化したい
ContextAPIなどを使っていると一見良さそうですが、パフォーマスが下がってしまったりします。コードが肥大化してしまい処理が複雑になってしまったりします。
そこでグローバル管理を最低限にするのがいいです。
必要なのがTanStackQuery
(旧React Query)です。
参考資料
-
ChatGPT
-
HapinessChainの無料特典