カテゴリ: React 更新日: 2026/03/30

React Context APIのレンダリング問題を解決!初心者でもわかるパフォーマンス最適化ガイド

Context APIで発生しやすいレンダリング問題と対処法
Context APIで発生しやすいレンダリング問題と対処法

先生と生徒の会話形式で理解しよう

生徒

「Reactのコンテキストを使ったら、関係ない部品まで一緒に動き出しちゃって、動作が重くなった気がします。これって防げますか?」

先生

「それは『不要な再描画(レンダリング)』という問題ですね。Reactの仕組み上、コンテキストの値が少しでも変わると、それを使っている全ての部品が描き直されてしまうんです。」

生徒

「全部描き直されちゃうんですか!必要なところだけ更新する方法はないんでしょうか?」

先生

「ありますよ。いくつかのコツを覚えるだけで、アプリの動作を劇的に軽くできます。具体的な対策を一緒に学んでいきましょう!」

1. レンダリング問題とは?なぜ重くなるのか

1. レンダリング問題とは?なぜ重くなるのか
1. レンダリング問題とは?なぜ重くなるのか

Reactにおけるレンダリングとは、プログラムの状態に合わせて、画面の見た目を「描き直す」作業のことです。本来、画面の一部が変わったとき、その変わった場所だけを書き換えれば効率が良いですよね。しかし、Reactの Context API(コンテキストエーピーアイ)を使うと、少し困ったことが起こります。

コンテキストは「共有の倉庫」のようなものですが、その倉庫の中にあるデータが一つでも更新されると、その倉庫を利用している全ての部品に対して「データが変わったから描き直して!」という命令が飛んでしまいます。例えば、ユーザー名と背景色の二つのデータを一つの倉庫に入れている場合、背景色を変えただけなのに、全く関係のないユーザー名を表示している部品まで描き直されてしまうのです。これが、動作が重くなる「不要なレンダリング」の正体です。

2. コンテキストを分割して影響を最小限にする

2. コンテキストを分割して影響を最小限にする
2. コンテキストを分割して影響を最小限にする

最も簡単で効果的な対策は、コンテキストを「役割ごと」に分けることです。一つの大きな倉庫に何でも詰め込むのではなく、野菜用の冷蔵庫、お肉用の冷蔵庫というように、専用の小さな倉庫を複数作るイメージです。これを「コンテキストの分割」と呼びます。

こうすることで、野菜を出し入れしてもお肉の冷蔵庫を使っている人には影響が出なくなります。プログラムも同じで、更新頻度や役割が違うデータは別のコンテキストに切り分けて管理しましょう。まずは、悪い例として一つのコンテキストにまとめた形を見てみます。


import React, { createContext, useState } from "react";

// 一つのコンテキストに、ユーザー名とテーマカラーを混ぜてしまった例
export const AppContext = createContext();

export function AppProvider({ children }) {
  const [user, setUser] = useState("たろう");
  const [theme, setTheme] = useState("light");

  return (
    <AppContext.Provider value={{ user, setUser, theme, setTheme }}>
      {children}
    </AppContext.Provider>
  );
}
(この書き方だと、themeを書き換えるだけでuserを表示している部品まで描き直されてしまいます。これを次に紹介する方法で分けていきます。)

3. 実践!コンテキストを分ける書き方

3. 実践!コンテキストを分ける書き方
3. 実践!コンテキストを分ける書き方

それでは、先ほどのコードを二つのコンテキストに分けてみましょう。これにより、テーマの色を変更したときに、ユーザー情報を表示している部品が余計な動きをすることを完全に防ぐことができます。

このように細かく分ける手法は、プログラミングの世界では「関心の分離」とも呼ばれます。初心者の方は「関係ないものは混ぜない」というシンプルなルールとして覚えておくだけで大丈夫です。


import React, { createContext, useState } from "react";

// ユーザー専用の倉庫
export const UserContext = createContext();
// テーマ専用の倉庫
export const ThemeContext = createContext();

export function MultiProvider({ children }) {
  const [user, setUser] = useState("たろう");
  const [theme, setTheme] = useState("light");

  return (
    <UserContext.Provider value={{ user, setUser }}>
      <ThemeContext.Provider value={{ theme, setTheme }}>
        {children}
      </ThemeContext.Provider>
    </UserContext.Provider>
  );
}
(コンテキストを二重に重ねることで、それぞれのデータが独立しました。テーマを変えてもユーザー情報の部品は静かなままになります。)

4. React.memoを使って部品を保護する

4. React.memoを使って部品を保護する
4. React.memoを使って部品を保護する

コンテキストの分割以外にも、部品そのものに「変更がないなら描き直さないで」と命令する方法があります。それが React.memo(リアクトメモ)です。これは、部品を薄い膜で包んで保護するようなイメージです。

親の部品が描き直されたとしても、その部品に渡されているデータが変わっていなければ、前回の見た目を再利用してくれます。これを使うことで、パソコンの計算量を減らし、滑らかな動きを維持できるようになります。特に行数が多い複雑な部品には、このメモ化という技術が非常に有効です。


import React, { memo } from "react";

// React.memoで部品を包みます
const ExpensiveComponent = memo(({ name }) => {
  console.log("重い処理が走りました");
  return <div>こんにちは、{name}さん!</div>;
});

export default ExpensiveComponent;
(この部品は、nameというデータが変わらない限り、何度親が描き直されても無視して自分の表示を使い回します。効率的ですね。)

5. 更新用関数を独立させるテクニック

5. 更新用関数を独立させるテクニック
5. 更新用関数を独立させるテクニック

コンテキストの問題として「データそのものは使っていないけれど、データを変えるボタンだけ持っている部品」も描き直しの対象になってしまう点があります。例えば、カウントを表示せず、「増やす」という命令だけを送るボタンです。

この場合、「現在の値」の倉庫と「更新するための関数」の倉庫を分けるという高等テクニックがあります。こうすると、値がどれだけ増えても、ボタン自体の見た目は変わらないため、ボタン部品が描き直されることはなくなります。


import React, { createContext, useState, useContext } from "react";

const CountStateContext = createContext();
const CountDispatchContext = createContext();

export function CountProvider({ children }) {
  const [count, setCount] = useState(0);

  return (
    <CountStateContext.Provider value={count}>
      <CountDispatchContext.Provider value={setCount}>
        {children}
      </CountDispatchContext.Provider>
    </CountStateContext.Provider>
  );
}
(値を管理する倉庫と、命令を管理する倉庫に分けました。これでボタンを連打しても、ボタン部品自体が余計な計算をすることはありません。)

6. useMemoで値の計算を節約する

6. useMemoで値の計算を節約する
6. useMemoで値の計算を節約する

コンテキストに渡すデータが、計算によって導き出される複雑なもの(例えば、大量のリストから特定の条件で絞り込んだ結果など)である場合、useMemo(ユーズメモ)という機能が役立ちます。

これは、計算結果を一時的に保存しておく「メモ用紙」のようなものです。毎回ゼロから計算し直すのではなく、「前回と同じ条件なら、この前計算した結果を使って」と指示を出せます。これにより、パソコンの頭脳であるシーピーユー(CPU)への負担を軽くし、バッテリーの持ちやスマホでの動作速度を改善できます。

7. プロバイダーを配置する場所に気をつける

7. プロバイダーを配置する場所に気をつける
7. プロバイダーを配置する場所に気をつける

最後のアドバイスは、コンテキストを提供する場所(Providerを置く場所)です。よく「とりあえず一番外側(App全体)を囲めばいいや」と考えがちですが、それはあまりおすすめできません。影響範囲が広がりすぎてしまうからです。

そのデータが必要な範囲が、特定の画面や一部のメニューだけなら、できるだけその近くに Provider を配置しましょう。囲む範囲を小さく保つことは、意図しない再描画を防ぐための最も基本的で強力な防衛策になります。パソコンを触ったことがない方でも、「大事なものは必要な時だけ、必要な人に渡す」と考えれば納得できるはずです。

8. パフォーマンス改善の第一歩として

8. パフォーマンス改善の第一歩として
8. パフォーマンス改善の第一歩として

ここまで、コンテキストのレンダリング問題を解決するための様々な方法を見てきました。最初は難しく感じるかもしれませんが、まずは「コンテキストを分ける」ことから始めてみてください。たったそれだけで、多くの問題は解決します。

Reactでの開発は、ただ動くものを作るだけでなく、ユーザーが「快適だ」と感じる速度を追求することも大切です。今回学んだ知識を活かして、軽快に動作する素晴らしいアプリを作っていきましょう。少しずつコードを書いて試していくことで、自然とレンダリングの仕組みが体に染み付いていくはずです。

カテゴリの一覧へ
新着記事
New1
React
React Context APIのレンダリング問題を解決!初心者でもわかるパフォーマンス最適化ガイド
New2
React
ReactのFetch APIでエラー処理を理解しよう!初心者でも安心のやさしい解説
New3
React
Reactでフォーカスイベントを制御する方法!onFocusとonBlurを初心者向けに解説
New4
Next.js
Next.js Pages RouterでのSSG(getStaticProps)完全解説!初心者でもわかる静的サイト生成の仕組み
人気記事
No.1
Java&Spring記事人気No1
React
Reactの子コンポーネントから親コンポーネントへデータを渡す方法を徹底解説!初心者にもわかるReactのイベントとデータの流れ
No.2
Java&Spring記事人気No2
React
Reactとは?初心者でもわかるReact.jsの基本概念と特徴をやさしく解説
No.3
Java&Spring記事人気No3
React
ReactのContext APIとReduxの違いを初心者向けに徹底比較!どちらを使うべきか完全解説
No.4
Java&Spring記事人気No4
React
ReactのState管理ベストプラクティス!初心者でもわかる再レンダリング最適化の考え方
No.5
Java&Spring記事人気No5
React
ReactのPresentational Componentを完全ガイド!初心者でもわかるStateを持たないコンポーネントの特徴
No.6
Java&Spring記事人気No6
React
React(TypeScript)で学ぶStateの型推論と型指定の違い!型安全なState管理を初心者向けにやさしく解説
No.7
Java&Spring記事人気No7
React
React開発におすすめのVSCode拡張機能まとめ!初心者でもすぐ使える便利ツール紹介
No.8
Java&Spring記事人気No8
Next.js
Next.jsのServer ComponentsとClient Componentsの通信方法を完全解説!props渡しの基本と使い方