【実務・中級編】 Context APIのパフォーマンス最適化 – React実践ガイド

Reactの現場で、Context APIを「ただのグローバルな状態管理ツール」として使っていませんか?

もし、アプリが巨大化するにつれて「どこかを変えると関係のないコンポーネントまで再レンダリングされて重くなる」という現象に悩んでいるなら、あなたはContextの落とし穴にハマっています。

今日は、Reactのスペシャリストとして、Context APIを本番環境で「殺さず」に使いこなすための、泥臭い最適化戦略を伝授します。

—

なぜContextは「再レンダリングの爆弾」になるのか

ReactのContextは、値が変わるとそのContextを購読している(`useContext`している)全コンポーネントを強制的に再レンダリングします。

ブラウザの裏側では、ReactがFiberツリーを辿り、Contextの依存関係にあるすべてのノードに対して「君のPropsは変わっていないかもしれないが、Contextが更新されたから再計算してくれ」と命令を下します。これがアプリ全体で起きれば、当然ボトルネックになります。

これを防ぐために重要なのは、「更新頻度」と「情報の種類」でコンテキストを分離するという設計思想です。

—

1. 状態と更新関数の分離(パフォーマンスの黄金則)

初心者がよくやるのが、`state`とそれを更新する`dispatch`(または関数)を一つのオブジェクトにまとめてContextに突っ込む方法です。

// 悪い例:これだとdispatchを呼ぶだけで、値を参照している全コンポーネントが再レンダリングされる
const UserContext = createContext({ user: {}, setUser: () => {} });

これだと、`setUser`だけ使いたいコンポーネントも、`user`の値が変わるたびに再レンダリングされます。これを解消するには、「値用」と「操作用」のContextを分けるのが鉄則です。

const UserStateContext = createContext(null);
const UserDispatchContext = createContext(null);

export const UserProvider = ({ children }) => {
const [user, setUser] = useState({ name: ‘Tanaka’, theme: ‘dark’ });

// useMemoで参照を固定する。これ重要。
const dispatch = useMemo(() => ({ setUser }), []);

return (


{children}


);
};

—

2. コンポーネントをラップして「レンダリングの壁」を作る

Contextを購読しているコンポーネントが巨大だと、再レンダリングのコストが跳ね上がります。そんな時は、「購読する部分」を小さなコンポーネントとして切り出すのが最も泥臭く、かつ確実な最適化です。

// 悪い例:コンポーネント全体でuseContextしている
const Profile = () => {
const { user } = useContext(UserContext); // ここで購読
return

{user.name}

; // 他の重い処理もここにあると巻き込まれる
};

// 良い例:購読専用のラッパーを作る
const UserName = () => {
const user = useContext(UserStateContext); // 必要最小限のデータだけ取得
return {user.name};
};

const Profile = () => {
return (

プロフィール

{/ ここだけが再レンダリングされる /}

);
};

—

3. 「セレクター」パターンで更新を抑制する(中級者向けTips)

もしContextの中身がオブジェクトで、特定のプロパティだけを監視したい場合、React標準にはReduxのような`useSelector`はありません。しかし、`useMemo`と組み合わせることで擬似的に実装できます。

const UserProfile = () => {
const user = useContext(UserStateContext);

// userオブジェクト全体を監視するのではなく、必要な値だけをmemo化して抽出
const name = useMemo(() => user.name, [user.name]);

return

{name}

;
};

ただし、これでも`user`オブジェクト自体が更新されれば`UserProfile`は再レンダリングされます。もし本当に「特定のプロパティ変更時のみ」反応させたいなら、Contextを細分化する(例:`UserNameContext`, `UserThemeContext`に分ける)のが、結局のところ最もReactの設計思想に沿った安全な道です。

—

まとめ:現場で意識すべきこと

1. Contextは「本当にグローバルなもの」だけに絞る: テーマ設定、ユーザー認証状態、i18nなど、アプリの根幹に関わるもの以外は、Prop Drillingを恐れずにコンポーネントへ渡すことも検討してください。
2. Providerを細分化する: 頻繁に変わる値と、ほとんど変わらない値は別のProviderに分けましょう。
3. メモ化を怠らない: `useMemo`や`useCallback`は、ContextのProviderに渡す値を安定させるための「糊(のり)」です。これを忘れるとProviderが更新されるたびに、子コンポーネントに伝播してしまいます。

Contextは強力な武器ですが、使い方を誤ると自らの足を撃ち抜くことになります。まずは「このContext、本当に全コンポーネントで購読する必要があるか?」と自問自答することから始めてみてください。

現場からは以上です。コードの質は、こうした地味な最適化の積み重ねで決まります。頑張りましょう。

コメント

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