🐕

【TypeScript + Next.js】 Context Provider を作ってみよう

に公開

Providerとは

  • React の Context API で使われるコンポーネント
  • データ(状態)をコンポーネントツリー全体に「供給(provide)」する役割
  • useContext とセットで使い、props drilling(深い階層への props 渡し)を解消できる

例:

<MyContext.Provider value={someValue}>
  <Child />
</MyContext.Provider>

基本構造

  1. Context 作成

    const MyContext = createContext(initialValue);
    
  2. Provider で囲む

    <MyContext.Provider value={someValue}>
      <App />
    </MyContext.Provider>
    
  3. 子コンポーネントで利用

    const value = useContext(MyContext);
    

よく使うパターン

  • 認証情報 (AuthProvider)
    → ログイン状態やユーザー情報を全体で共有
  • テーマ管理 (ThemeProvider)
    → ダークモード / ライトモードの切り替え
  • 状態管理ライブラリと組み合わせ
    → Redux の <Provider> や React Query の <QueryClientProvider>

📌 UserContext の例

1. Context 定義

// contexts/UserContext.tsx
"use client";
import { createContext, useContext, useState, ReactNode } from "react";

type User = {
  id: string;
  name: string;
  email: string;
} | null;

type UserContextType = {
  user: User;
  setUser: (user: User) => void;
};

// Context の作成
const UserContext = createContext<UserContextType | undefined>(undefined);

// Provider コンポーネント
export function UserProvider({ children }: { children: ReactNode }) {
  const [user, setUser] = useState<User>(null);

  return (
    <UserContext.Provider value={{ user, setUser }}>
      {children}
    </UserContext.Provider>
  );
}

// ✅ より使いやすくするためのカスタムフック
export function useUser() {
  const context = useContext(UserContext);
  if (!context) {
    throw new Error("useUser は UserProvider 内で使用してください");
  }
  return context;
}

2. アプリ全体を Provider でラップ

// app/layout.tsx (Next.js 13+)
import { UserProvider } from "@/contexts/UserContext";

export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <html lang="ja">
      <body>
        <UserProvider>
          {children}
        </UserProvider>
      </body>
    </html>
  );
}

3. コンポーネントで利用

// app/page.tsx
"use client";
import { useUser } from "@/contexts/UserContext";

export default function HomePage() {
  const { user, setUser } = useUser();

  return (
    <main>
      {user ? (
        <>
          <h1>こんにちは、{user.name} さん 👋</h1>
          <button onClick={() => setUser(null)}>ログアウト</button>
        </>
      ) : (
        <>
          <h1>ログインしてください</h1>
          <button
            onClick={() =>
              setUser({ id: "1", name: "ヒョニ", email: "hyoni@example.com" })
            }
          >
            ログイン
          </button>
        </>
      )}
    </main>
  );
}

✅ 動作の流れ

  1. UserProvider がアプリ全体で user 状態を管理
  2. useUser() を使うと、どのコンポーネントからでもユーザー情報にアクセス可能
  3. HomePage のようなコンポーネントでは props を渡さなくても直接利用できる

知っておくべきこと

  • Provider はネストできる
    複数の Context を同時に使える

  • value が変わると子コンポーネントが再レンダーされる → パフォーマンス注意

  • カスタムフックと組み合わせると便利

    const useAuth = () => useContext(AuthContext);
    
  • 必要以上に多用しない
    状態がローカルで済む場合は useState で十分


Discussion