【実務・中級編】 Context APIの基本概念とプロバイダー – React実践ガイド

ReactのContext APIを「ただの便利機能」で終わらせないための、現場で使える設計術

Reactを触り始めてしばらく経つと、誰もが一度はぶつかる壁がある。「propsのバケツリレー」だ。コンポーネントを深く掘れば掘るほど、親から子、孫へとただの通過点として渡されるpropsたち。これを見て「Contextを使えば解決だ!」と飛びつくのは、中級者への第一歩としては正しい。

だが、ここで立ち止まってほしい。Contextは魔法の杖ではない。使い方を誤れば、アプリケーション全体が「謎の再レンダリング地獄」に陥る諸刃の剣だ。今日は、このContextを実務レベルで正しく、かつ美しく扱うための作法を語ろう。

—

Contextの正体と、Reactが裏側で行っていること

ReactのContextは、依存関係の注入(DI)の一種だ。技術的に言えば、コンポーネントツリーに「値の供給源(Provider)」を配置し、そこからツリーの下層に向けて、プロップスを介さずにデータを「直接注入」する仕組みだ。

ここで重要なのは、ブラウザ(React)がどう処理しているかという点だ。
Contextの値を変更すると、そのProviderを親に持つすべての消費コンポーネント(useContextを使っている箇所)が再レンダリングされる。これは、Reactのレンダリング最適化の基本ルールである「propsの変化」とは別に、Contextの更新通知がツリーを駆け下りるからだ。

だからこそ、Contextに「頻繁に変わる値」を詰め込むのは悪手だ。ログインユーザー情報やテーマ設定といった、「めったに変わらない大域的なデータ」を置くのが定石である。

—

実践的サンプルコード:型安全なContextの構築

現場で「とりあえず`any`でいいや」なんてコードを書いたら、即座にコードレビューで差し戻す。TypeScriptを駆使し、責務を分離した綺麗な実装を見てほしい。

import React, { createContext, useContext, useState, ReactNode } from ‘react’;

// 1. Contextで管理するデータの型を定義
type AuthContextType = {
user: { name: string; email: string } | null;
login: (name: string) => void;
logout: () => void;
};

// 2. Contextの作成。初期値はnullチェックを強制するためにあえてnullにする
const AuthContext = createContext(null);

// 3. Providerをラップしたコンポーネントを作成
// これにより、ロジックをコンポーネントツリーから隠蔽できる
export const AuthProvider = ({ children }: { children: ReactNode }) => {
const [user, setUser] = useState(null);

const login = (name: string) => setUser({ name, email: `${name}@example.com` });
const logout = () => setUser(null);

return (

{children}

);
};

// 4. カスタムフックでContextを消費。Provider外で使った時のエラーハンドリングが重要
export const useAuth = () => {
const context = useContext(AuthContext);
if (!context) {
throw new Error(‘useAuthはAuthProvider内で使用してください’);
}
return context;
};

なぜこの形が「現場のベスト」なのか

  • 型安全: `useContext`を直接叩かず、カスタムフック経由にすることで、型定義を一元化している。
  • 堅牢性: `useAuth`で`null`チェックを行うことで、開発中の「Contextを忘れた」というミスを即座にランタイムエラーとして検知できる。
  • 関心の分離: `AuthProvider`の中にロジックを閉じ込めることで、`App.tsx`が肥大化するのを防げる。

—

運用上の注意:再レンダリングの罠を避ける

「Contextは便利だが、多用するな」という教えには理由がある。

もし、ひとつのContextに巨大なオブジェクトを突っ込んで、その中のたった一つのプロパティが変わるたびに、ツリー全体が再レンダリングされたらどうなるか? アプリケーションは重くなり、ユーザー体験は損なわれる。

実務での鉄則:
1. Contextを細分化せよ: 「ユーザー情報用」と「UI設定用」のように、更新頻度や目的が異なるものは別のProviderに分ける。
2. `useMemo`を有効活用せよ: Providerに渡す `value` オブジェクトを、`useMemo` でラップすることで、不要な再レンダリングを抑止できる。
3. React Query (TanStack Query) の検討: もしAPI通信の結果をContextに入れているなら、それはContextの責務ではない。キャッシュ管理が得意なライブラリに任せるべきだ。

—

最後に:職人としての心得

Contextは、Reactの道具箱の中でも非常に強力な一つだ。しかし、道具が強力であればあるほど、使い手の技量が問われる。

「どこでもデータが取れるから」と安易にContextを乱用すると、コンポーネントのテストが困難になり、データフローが追いづらくなる。まずはpropsで解決できないか考え、それでもなお「これはアプリ全体で共有すべきインフラ的なデータだ」と確信した時だけ、Contextの扉を開いてほしい。

コードは、常に「次に読む人」のことを考えて書くこと。それが、君がプロのエンジニアとして成長するための最短ルートだ。現場で困ったときは、またいつでも相談してくれ。

コメント

タイトルとURLをコピーしました