Interactive

Context

ReactのContextはグローバル変数に等しいため、ClaudeCodeでも限定的な用途でのみ使用します。弊社では、多言語設定、ログイン状態、ユーザー情報など本当にグローバルな状態か、エディタなど複雑なコンポーネントでのProps Drilling回避でのみContextを使用します。

弊社の推奨ルール

  • Contextは基本的に使用しない(グローバル変数と同等のため)
  • 多言語、認証状態、ログイン中のユーザー情報のみ許可
  • 複雑なコンポーネント(エディタ等)でのバケツリレー回避に使用
  • ContextにはsetStateも含めて更新可能にする
  • 複雑な状態更新はReducerと組み合わせる
  • Contextの初期値にはProxyを使用してProvider設定忘れを検出する

ClaudeCodeでの利用

認証状態の管理

ログイン状態とユーザー情報を管理する基本的な用途

「ログイン状態とユーザー情報を管理するContextを作成して、setStateも含めて」

多言語対応の実装

アプリ全体で使用する言語設定を管理する

「多言語対応のContextを作成して、言語切り替え機能も含めて」

エディタコンポーネントでの使用

複雑なコンポーネントでバケツリレーを回避する

「エディタコンポーネント用のContextでバケツリレーを回避して」

ReducerとContextの組み合わせ

複雑な状態更新が必要な場合の実装

「認証ContextをReducerと組み合わせて複雑な状態管理を実装」

ProviderなしでContextを使用した場合のエラー検出

Contextの初期値にProxyを使用することで、Providerが設定されていない状態でContextを使用した場合に明確なエラーを発生させます。これにより、開発時にProviderの設定忘れを早期に発見できます。

// Context用の型定義
type AppContextValue = {
  state: AppState;
  dispatch: (action: AppAction) => void;
};

// ProviderなしでContext使用時にエラーを投げるProxy
const proxy = new Proxy({} as AppContextValue, {
  get() {
    throw new Error("AppContext must be provided");
  }
});

// Contextの初期値にProxyを設定
export const AppContext = createContext<AppContextValue>(proxy);

このProxyパターンにより、Providerでラップされていないコンポーネントがcontextの任意のプロパティにアクセスしようとすると、即座にエラーが発生します。

// 使用例:認証Context
type AuthContextValue = {
  isAuthenticated: boolean;
  signOut(): Promise<null>;
  signIn(email: string, password: string): Promise<null>;
  refresh(): Promise<null>;
};

const authProxy = new Proxy({} as AuthContextValue, {
  get() {
    throw new Error("AuthContext must be provided");
  }
});

export const AuthContext = createContext<AuthContextValue>(authProxy);

// ❌ Providerなしで使用するとエラー
function ComponentWithoutProvider() {
  const { isAuthenticated } = useContext(AuthContext); 
  // Error: AuthContext must be provided (プロパティアクセス時に発生)
  return <div>{isAuthenticated ? "ログイン済み" : "未ログイン"}</div>;
}

// ✅ Provider内で使用すれば正常動作
function App() {
  const authValue: AuthContextValue = {
    isAuthenticated: true,
    signOut: async () => null,
    signIn: async () => null,
    refresh: async () => null
  };
  
  return (
    <AuthContext.Provider value={authValue}>
      <ComponentWithProvider />
    </AuthContext.Provider>
  );
}

弊社では、すべてのContextでこのProxyパターンを使用し、Provider設定忘れによるバグを防いでいます。

setStateを含めた更新可能なContext

Contextには値だけでなくsetStateも含めることで、どのコンポーネントからも状態を更新できるようにします。

// 認証Contextの例(setStateを含む)
type AuthContextValue = {
  user: User | null;
  isAuthenticated: boolean;
  setUser: (user: User | null) => void;
  setIsAuthenticated: (value: boolean) => void;
};

function AuthProvider({ children }: { children: ReactNode }) {
  const [user, setUser] = useState<User | null>(null);
  const [isAuthenticated, setIsAuthenticated] = useState(false);
  
  const value: AuthContextValue = {
    user,
    isAuthenticated,
    setUser,
    setIsAuthenticated
  };
  
  return (
    <AuthContext.Provider value={value}>
      {children}
    </AuthContext.Provider>
  );
}

よくある問題

Contextを使うべきでない場合

テーマ設定などの頻繁に変わらない値以外は、基本的にContextを避けます。フォームの状態、UIの開閉状態などローカルな状態は、propsやカスタムフックで管理します。

グローバル変数としての認識不足

Contextはグローバル変数と同等です。弊社では、多言語設定、認証状態、ユーザー情報以外での使用を原則禁止しています。

エディタなど複雑なコンポーネントでの使用

エディタのような深い階層を持つ複雑なコンポーネントでは、Props Drilling回避のためContextを使用します。ただし、エディタコンポーネント内でのみ使用し、アプリ全体には影響しないよう設計します。