ReactのContext APIで簡単にグローバルステートを管理する方法と実践例

React開発では、複数のコンポーネント間でデータを共有する際、効率的にグローバルステートを管理することが重要です。その解決策の一つとして注目されているのが、Reactに組み込まれている「Context API」です。Reduxなどの外部ライブラリを導入することなく、シンプルな方法でステート管理を実現できるため、小中規模のアプリケーションや特定の機能に最適です。本記事では、Context APIの基本的な使い方から実践例までを通して、グローバルステート管理の具体的な手法を分かりやすく解説します。

目次
  1. Context APIとは
    1. Context APIの役割
    2. Context APIの利用シーン
    3. 基本の構造
  2. グローバルステート管理の重要性
    1. グローバルステートとは
    2. グローバルステート管理が重要な理由
    3. Context APIが解決する課題
    4. 具体例で考えるグローバルステート管理
  3. Context APIとReduxの比較
    1. Context APIとReduxの概要
    2. 違いを徹底比較
    3. どちらを選ぶべきか?
  4. Contextの作成手順
    1. 1. Contextの生成
    2. 2. Providerの設定
    3. 3. ConsumerまたはuseContextの利用
    4. 4. Contextをコンポーネントに適用する
    5. まとめ
  5. 実践例:簡易的なTodoリストアプリ
    1. Context APIを使ったグローバルステート管理の例
    2. 1. プロジェクトのセットアップ
    3. 2. Contextの作成
    4. 3. コンポーネントの実装
    5. 4. アプリケーションの統合
    6. 5. 実行
    7. まとめ
  6. コンポーネント構成とContextの適用
    1. Contextを用いたコンポーネントの分割
    2. 1. コンポーネント階層
    3. 2. Providerの適用
    4. 3. TodoInputでのContext使用
    5. 4. TodoListでのContext使用
    6. 5. TodoItemでのContext使用
    7. 6. コンポーネント間での連携
    8. まとめ
  7. パフォーマンス最適化のポイント
    1. Context API利用時のパフォーマンス課題
    2. 1. Providerの分割
    3. 2. メモ化の活用
    4. 3. コンポーネントの分離
    5. 4. 適切なコンテキスト設計
    6. 5. 開発ツールでの検証
    7. まとめ
  8. トラブルシューティング
    1. Context API利用時によくあるエラーと解決策
    2. 1. Providerを忘れる問題
    3. 2. 値のメモ化忘れによる不要な再レンダリング
    4. 3. 複数のContextの混同
    5. 4. Providerの多重ネスト問題
    6. 5. コンテキスト値の初期化不足
    7. 6. パフォーマンスの低下
    8. まとめ
  9. まとめ

Context APIとは


Context APIは、Reactが提供するステート管理の仕組みで、親コンポーネントから子コンポーネントにプロパティを手渡す「プロップス・ドリリング」を避けるために設計されています。この機能を利用すると、複数のコンポーネント間で簡単にデータを共有できるようになります。

Context APIの役割


Context APIは以下の役割を果たします。

  • グローバルなデータ(テーマ、認証情報、ユーザー設定など)を効率的に共有する。
  • 階層の深いコンポーネント間でのデータ受け渡しを簡素化する。

Context APIの利用シーン


Context APIは、以下のような場合に有効です。

  • テーマの管理:ライトモードとダークモードの切り替えをアプリ全体に反映させる。
  • 認証情報の共有:ログインユーザーの情報を複数のコンポーネントで使用する。
  • 設定情報の適用:言語設定や地域設定をグローバルに適用する。

基本の構造


Context APIの基本的な使い方は以下の3ステップで構成されます。

  1. Contextの作成
    React.createContext()でContextを生成します。
  2. Providerの利用
    Contextを通じて値を供給するために、Providerを使用します。
  3. ConsumerまたはuseContextの利用
    値を消費するために、ConsumerコンポーネントやuseContextフックを使います。

Context APIは、外部ライブラリを導入せずに簡潔なグローバルステート管理を実現する強力なツールです。

グローバルステート管理の重要性

グローバルステートとは


グローバルステートとは、アプリケーション全体または複数のコンポーネント間で共有されるデータのことを指します。このステートを管理することで、データの一貫性を保ちつつ、コンポーネント間の連携を容易にします。

グローバルステート管理が重要な理由


グローバルステートを適切に管理することは、次のような理由で重要です。

  • データの一貫性:全体で共有される値が変更された場合、関連するすべてのコンポーネントに即座に反映されます。
  • コードの簡素化:プロップス・ドリリングを回避し、コードの可読性を向上させます。
  • 保守性の向上:一箇所で管理されたステートは、変更や拡張が容易になります。

Context APIが解決する課題


Context APIは以下の課題を解決します。

  • プロップス・ドリリングの問題:深いコンポーネント階層でデータを渡す際の冗長なコードを削減します。
  • 複数のステート管理ライブラリの複雑さ:ReduxやMobXなどの外部ライブラリに比べ、軽量かつシンプルに運用できます。

具体例で考えるグローバルステート管理


例えば、ユーザー認証の状態を管理する場合、グローバルステートを利用することで、ログイン情報を複数のコンポーネントで簡単に共有できます。この際、Context APIを利用すれば、特定の認証情報をどの階層からでも直接取得できます。

グローバルステート管理の適切な実装は、アプリケーション全体のパフォーマンスや開発効率を向上させる重要な要素です。

Context APIとReduxの比較

Context APIとReduxの概要


Context APIとReduxは、どちらもReactアプリケーションでのステート管理を目的としていますが、設計思想や用途に違いがあります。

  • Context API: Reactに標準搭載されており、軽量なグローバルステート管理を提供します。主に小中規模のプロジェクトや特定のステート共有が必要な場面に適しています。
  • Redux: 外部ライブラリで、厳密なステート管理フロー(アクション、リデューサー)を採用しています。大規模なアプリケーションや複雑なステートロジックを必要とする場合に適しています。

違いを徹底比較

使いやすさ

  • Context API: 専用の設定が不要で、ProvideruseContextフックで簡単に実装可能です。
  • Redux: 初期設定が必要で、アクションやリデューサーなどの記述が複雑です。学習コストが高くなります。

パフォーマンス

  • Context API: 値が変更されると、関連する全てのコンシューマーが再レンダリングされるため、大量のコンポーネントに影響を与える可能性があります。
  • Redux: 独自のストアとサブスクリプションメカニズムを利用しており、変更が必要な部分にのみレンダリングを制限できます。

スケーラビリティ

  • Context API: 小中規模のアプリケーションや特定のステート共有に最適ですが、複雑な状態管理には向いていません。
  • Redux: 状態管理の分離やミドルウェアの活用により、大規模アプリケーションでも柔軟に対応できます。

どちらを選ぶべきか?


Context APIとReduxの選択基準は、プロジェクトの規模や要件によって異なります。

  • Context APIを選ぶべき場合
  • アプリケーションが比較的小規模である。
  • 複雑なステートロジックを必要としない。
  • Reduxの学習コストを抑えたい。
  • Reduxを選ぶべき場合
  • アプリケーションが大規模であり、多数のステートが絡む。
  • 状態の管理が複雑で、厳密なフローが必要。
  • サードパーティのミドルウェアを活用する場面がある。

Context APIとReduxはそれぞれの特性を理解し、適切に使い分けることで、より効率的なReact開発を実現できます。

Contextの作成手順

1. Contextの生成


まず、React.createContext()を使用して新しいContextを作成します。このContextは、グローバルステートの定義に使います。

import React, { createContext } from 'react';

// Contextの作成
export const MyContext = createContext();

2. Providerの設定


次に、Providerを利用して、Contextをアプリケーション内で利用可能にします。Providerは値を供給し、コンポーネント階層全体に渡します。

import React, { useState } from 'react';
import { MyContext } from './MyContext';

export const MyProvider = ({ children }) => {
  const [state, setState] = useState('Hello, Context!');

  return (
    <MyContext.Provider value={{ state, setState }}>
      {children}
    </MyContext.Provider>
  );
};

3. ConsumerまたはuseContextの利用


作成したContextをコンポーネントで利用するには、以下の方法を選択します。

方法1: `useContext`フックを使用する


より簡潔に値を取得する方法として、useContextフックを使用します。

import React, { useContext } from 'react';
import { MyContext } from './MyContext';

const MyComponent = () => {
  const { state, setState } = useContext(MyContext);

  return (
    <div>
      <p>{state}</p>
      <button onClick={() => setState('Updated Context!')}>Update</button>
    </div>
  );
};

方法2: `Consumer`コンポーネントを使用する


React 16以前のプロジェクトや、関数型コンポーネントがない場合に利用できます。

import React from 'react';
import { MyContext } from './MyContext';

const MyComponent = () => (
  <MyContext.Consumer>
    {({ state, setState }) => (
      <div>
        <p>{state}</p>
        <button onClick={() => setState('Updated Context!')}>Update</button>
      </div>
    )}
  </MyContext.Consumer>
);

4. Contextをコンポーネントに適用する


作成したProviderをアプリケーションのルートまたは必要な部分にラップします。

import React from 'react';
import { MyProvider } from './MyContext';
import MyComponent from './MyComponent';

const App = () => (
  <MyProvider>
    <MyComponent />
  </MyProvider>
);

export default App;

まとめ


この手順を使えば、Context APIを活用してグローバルステートを簡単に管理できます。これにより、Reactアプリケーション全体での効率的なデータ共有が可能になります。

実践例:簡易的なTodoリストアプリ

Context APIを使ったグローバルステート管理の例


ここでは、Context APIを活用してTodoリストアプリを作成します。このアプリでは以下の機能を実装します:

  1. Todoアイテムの追加
  2. Todoアイテムの削除
  3. Todoリストの全コンポーネント間での共有

1. プロジェクトのセットアップ


以下のコマンドで新しいReactプロジェクトを作成します:

npx create-react-app todo-app
cd todo-app

必要なディレクトリとファイルを作成します:

src/
  ├── components/
  │     ├── TodoInput.js
  │     ├── TodoList.js
  │     └── TodoItem.js
  ├── context/
  │     └── TodoContext.js
  └── App.js

2. Contextの作成


Todoリスト用のContextを作成します。

src/context/TodoContext.js

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

// Contextを作成
export const TodoContext = createContext();

// Providerを定義
export const TodoProvider = ({ children }) => {
  const [todos, setTodos] = useState([]);

  // Todoを追加
  const addTodo = (text) => {
    setTodos([...todos, { id: Date.now(), text }]);
  };

  // Todoを削除
  const removeTodo = (id) => {
    setTodos(todos.filter(todo => todo.id !== id));
  };

  return (
    <TodoContext.Provider value={{ todos, addTodo, removeTodo }}>
      {children}
    </TodoContext.Provider>
  );
};

3. コンポーネントの実装

src/components/TodoInput.js
Todoの入力フォームを作成します。

import React, { useState, useContext } from 'react';
import { TodoContext } from '../context/TodoContext';

const TodoInput = () => {
  const [text, setText] = useState('');
  const { addTodo } = useContext(TodoContext);

  const handleSubmit = (e) => {
    e.preventDefault();
    if (text.trim()) {
      addTodo(text);
      setText('');
    }
  };

  return (
    <form onSubmit={handleSubmit}>
      <input 
        type="text" 
        value={text} 
        onChange={(e) => setText(e.target.value)} 
        placeholder="Add a new task" 
      />
      <button type="submit">Add</button>
    </form>
  );
};

export default TodoInput;

src/components/TodoList.js
Todoリストを表示するコンポーネントを作成します。

import React, { useContext } from 'react';
import { TodoContext } from '../context/TodoContext';
import TodoItem from './TodoItem';

const TodoList = () => {
  const { todos } = useContext(TodoContext);

  return (
    <ul>
      {todos.map(todo => (
        <TodoItem key={todo.id} todo={todo} />
      ))}
    </ul>
  );
};

export default TodoList;

src/components/TodoItem.js
個別のTodoアイテムを表示します。

import React, { useContext } from 'react';
import { TodoContext } from '../context/TodoContext';

const TodoItem = ({ todo }) => {
  const { removeTodo } = useContext(TodoContext);

  return (
    <li>
      {todo.text}
      <button onClick={() => removeTodo(todo.id)}>Delete</button>
    </li>
  );
};

export default TodoItem;

4. アプリケーションの統合


src/App.js
作成したコンポーネントとProviderを統合します。

import React from 'react';
import { TodoProvider } from './context/TodoContext';
import TodoInput from './components/TodoInput';
import TodoList from './components/TodoList';

const App = () => {
  return (
    <TodoProvider>
      <div>
        <h1>Todo List</h1>
        <TodoInput />
        <TodoList />
      </div>
    </TodoProvider>
  );
};

export default App;

5. 実行


プロジェクトを起動して、動作を確認します。

npm start

ブラウザでhttp://localhost:3000にアクセスし、Todoリストアプリが正常に動作していることを確認します。

まとめ


この実践例では、Context APIを活用してシンプルでスケーラブルなTodoリストアプリを構築しました。この手法を応用することで、複雑なステート管理が求められるReactアプリケーションにも対応可能です。

コンポーネント構成とContextの適用

Contextを用いたコンポーネントの分割


Context APIの効果を最大化するには、適切にコンポーネントを分割し、役割を明確にすることが重要です。このセクションでは、実践例のTodoリストアプリを基に、Contextの適用方法とコンポーネント構成を詳しく解説します。

1. コンポーネント階層


Todoリストアプリのコンポーネント構成は次のようになっています:

  • App
  • Context Provider(TodoProvider)でラップする。
  • 子コンポーネントにグローバルステートを共有。
  • TodoInput
  • 新しいTodoを追加する入力フォーム。
  • addTodo関数を使用して新しいタスクを登録。
  • TodoList
  • 全Todoアイテムを表示するコンポーネント。
  • todos配列を受け取り、各TodoをTodoItemコンポーネントで表示。
  • TodoItem
  • 単一のTodoアイテムを表示し、削除操作を提供。
  • removeTodo関数を使用して特定のタスクを削除。

2. Providerの適用


AppコンポーネントでTodoProviderを適用し、Contextを全コンポーネントに供給します。

import React from 'react';
import { TodoProvider } from './context/TodoContext';
import TodoInput from './components/TodoInput';
import TodoList from './components/TodoList';

const App = () => (
  <TodoProvider>
    <div>
      <h1>Todo List</h1>
      <TodoInput />
      <TodoList />
    </div>
  </TodoProvider>
);

export default App;

3. TodoInputでのContext使用


TodoInputコンポーネントは、useContextフックを用いて、addTodo関数を利用します。これにより、グローバルステートに新しいTodoを追加できます。

import React, { useState, useContext } from 'react';
import { TodoContext } from '../context/TodoContext';

const TodoInput = () => {
  const [text, setText] = useState('');
  const { addTodo } = useContext(TodoContext);

  const handleSubmit = (e) => {
    e.preventDefault();
    if (text.trim()) {
      addTodo(text);
      setText('');
    }
  };

  return (
    <form onSubmit={handleSubmit}>
      <input 
        type="text" 
        value={text} 
        onChange={(e) => setText(e.target.value)} 
        placeholder="Add a new task" 
      />
      <button type="submit">Add</button>
    </form>
  );
};

export default TodoInput;

4. TodoListでのContext使用


TodoListコンポーネントは、useContextフックを用いて、グローバルステートのtodos配列を取得します。これをマッピングして、TodoItemを描画します。

import React, { useContext } from 'react';
import { TodoContext } from '../context/TodoContext';
import TodoItem from './TodoItem';

const TodoList = () => {
  const { todos } = useContext(TodoContext);

  return (
    <ul>
      {todos.map(todo => (
        <TodoItem key={todo.id} todo={todo} />
      ))}
    </ul>
  );
};

export default TodoList;

5. TodoItemでのContext使用


TodoItemコンポーネントでは、削除ボタンがクリックされたときに、removeTodo関数を呼び出します。

import React, { useContext } from 'react';
import { TodoContext } from '../context/TodoContext';

const TodoItem = ({ todo }) => {
  const { removeTodo } = useContext(TodoContext);

  return (
    <li>
      {todo.text}
      <button onClick={() => removeTodo(todo.id)}>Delete</button>
    </li>
  );
};

export default TodoItem;

6. コンポーネント間での連携


TodoInputで新しいタスクを追加すると、グローバルステートが更新されます。TodoListTodoItemはこの更新を自動的に反映します。Context APIにより、プロップス・ドリリングを回避し、コンポーネント間の連携がスムーズになります。

まとめ


コンポーネント構成を明確にし、Contextを適用することで、Reactアプリケーションにおけるグローバルステート管理が簡潔になります。この手法は、アプリのスケールに応じて柔軟に対応可能です。

パフォーマンス最適化のポイント

Context API利用時のパフォーマンス課題


Context APIは便利なステート管理手法ですが、利用方法を誤るとパフォーマンス低下を招く場合があります。特に、Providerで提供される値が更新されるたびに、関連するすべてのコンシューマーが再レンダリングされることが問題です。ここでは、これを防ぎ、パフォーマンスを最適化するための具体的なテクニックを解説します。

1. Providerの分割


異なる種類のステートを1つのProviderで管理すると、不要な再レンダリングが発生します。これを回避するために、ステートごとにProviderを分割することを検討してください。

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

export const ThemeContext = createContext();
export const UserContext = createContext();

export const ThemeProvider = ({ children }) => {
  const [theme, setTheme] = useState('light');
  return (
    <ThemeContext.Provider value={{ theme, setTheme }}>
      {children}
    </ThemeContext.Provider>
  );
};

export const UserProvider = ({ children }) => {
  const [user, setUser] = useState(null);
  return (
    <UserContext.Provider value={{ user, setUser }}>
      {children}
    </UserContext.Provider>
  );
};

これにより、テーマ変更時にユーザー関連のコンポーネントが再レンダリングされることを防げます。

2. メモ化の活用


useMemouseCallbackを利用して、Providerに渡す値や関数をメモ化すると、不要な再レンダリングを抑制できます。

import React, { createContext, useState, useMemo } from 'react';

export const CounterContext = createContext();

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

  const value = useMemo(() => ({ count, setCount }), [count]);

  return (
    <CounterContext.Provider value={value}>
      {children}
    </CounterContext.Provider>
  );
};

これにより、countが変更されたときだけvalueが更新されます。

3. コンポーネントの分離


Contextを利用するコンポーネントが増えると、1つの値の更新が広範囲に影響を及ぼします。React.memoを活用して、コンポーネントの不要な再レンダリングを防ぎます。

import React, { useContext, memo } from 'react';
import { CounterContext } from './CounterContext';

const CounterDisplay = memo(() => {
  const { count } = useContext(CounterContext);
  return <p>Current Count: {count}</p>;
});

export default CounterDisplay;

これにより、countが変更された場合でも、依存しないコンポーネントの再レンダリングが発生しなくなります。

4. 適切なコンテキスト設計


すべてのステートをContext APIで管理する必要はありません。頻繁に変更されるステート(例: 入力フォームの値)は、ローカルステートとして管理する方が効率的です。

const Form = () => {
  const [inputValue, setInputValue] = useState('');

  const handleChange = (e) => setInputValue(e.target.value);

  return <input value={inputValue} onChange={handleChange} />;
};

頻繁な変更を伴う値をローカルステートにすると、Contextによる再レンダリングの影響を最小限に抑えられます。

5. 開発ツールでの検証


React開発ツールを利用して、再レンダリングの頻度を確認します。「Highlight Updates」にチェックを入れることで、どのコンポーネントが再レンダリングされたかを視覚的に確認可能です。

まとめ


Context APIの利便性を最大限に活かすには、パフォーマンスの課題を認識し、適切な設計とツールの活用が不可欠です。Providerの分割、値のメモ化、React.memoの利用などを組み合わせることで、Reactアプリケーションのパフォーマンスを効率的に最適化できます。

トラブルシューティング

Context API利用時によくあるエラーと解決策


Context APIを使用する際、設定や使用方法を誤るとエラーが発生することがあります。ここでは、よくある問題とその解決策を説明します。

1. Providerを忘れる問題


エラー例:
TypeError: Cannot read properties of undefined (reading 'value')

このエラーは、コンポーネントがProviderでラップされていない場合に発生します。

原因:
useContextまたはConsumerを使用する際、コンポーネントが対応するProviderの外側で呼び出されています。

解決策:
Providerを使用し、コンポーネント階層全体をラップしてください。

import React from 'react';
import { MyProvider } from './MyContext';
import MyComponent from './MyComponent';

const App = () => (
  <MyProvider>
    <MyComponent />
  </MyProvider>
);

export default App;

2. 値のメモ化忘れによる不要な再レンダリング


問題:
Providerが渡す値が頻繁に再計算され、関連コンポーネントが再レンダリングされる。

解決策:
useMemoまたはuseCallbackを使用して、値や関数をメモ化します。

import React, { createContext, useState, useMemo } from 'react';

export const MyContext = createContext();

export const MyProvider = ({ children }) => {
  const [state, setState] = useState('example');

  const value = useMemo(() => ({ state, setState }), [state]);

  return <MyContext.Provider value={value}>{children}</MyContext.Provider>;
};

3. 複数のContextの混同


問題:
複数のContextを使用している場合、異なるContextを取り間違えると、値が取得できません。

解決策:
Contextの命名を明確にし、それぞれの用途を分けて管理してください。また、TypeScriptを使用して型を定義すると、エラーを防ぎやすくなります。

export const ThemeContext = createContext();
export const UserContext = createContext();

4. Providerの多重ネスト問題


問題:
複数のProviderがネストされ、コードの可読性が低下する。

解決策:
カスタムのProviderコンポーネントを作成し、複数のProviderを1つに統合します。

export const AppProviders = ({ children }) => (
  <ThemeProvider>
    <UserProvider>
      {children}
    </UserProvider>
  </ThemeProvider>
);

5. コンテキスト値の初期化不足


問題:
useContextを呼び出した際に、初期化されていない値を参照してエラーが発生。

解決策:
Contextの初期値を設定します。

export const MyContext = createContext({
  state: null,
  setState: () => {},
});

6. パフォーマンスの低下


問題:
大規模なアプリケーションで、値の更新が頻繁に発生し、再レンダリングが多発。

解決策:

  • 値の更新を必要最小限にする。
  • コンテキストを分割して影響範囲を限定する。

まとめ


Context APIを活用する際には、Providerの適切な設定やパフォーマンスの最適化、複数Contextの整理が重要です。エラーが発生した場合は、エラーメッセージを分析し、ここで紹介したトラブルシューティングを試してください。これにより、Context APIを安全かつ効率的に利用できます。

まとめ


本記事では、ReactのContext APIを利用したグローバルステート管理について、その基本から実践例、パフォーマンスの最適化方法、トラブルシューティングまでを詳しく解説しました。Context APIは、外部ライブラリを導入せずにシンプルなステート管理を可能にする強力なツールです。適切な設計と実装を行うことで、アプリケーション全体の効率性と保守性を向上させることができます。今回の内容を活用して、よりスケーラブルで使いやすいReactアプリケーションを構築してみてください。

コメント

コメントする

目次
  1. Context APIとは
    1. Context APIの役割
    2. Context APIの利用シーン
    3. 基本の構造
  2. グローバルステート管理の重要性
    1. グローバルステートとは
    2. グローバルステート管理が重要な理由
    3. Context APIが解決する課題
    4. 具体例で考えるグローバルステート管理
  3. Context APIとReduxの比較
    1. Context APIとReduxの概要
    2. 違いを徹底比較
    3. どちらを選ぶべきか?
  4. Contextの作成手順
    1. 1. Contextの生成
    2. 2. Providerの設定
    3. 3. ConsumerまたはuseContextの利用
    4. 4. Contextをコンポーネントに適用する
    5. まとめ
  5. 実践例:簡易的なTodoリストアプリ
    1. Context APIを使ったグローバルステート管理の例
    2. 1. プロジェクトのセットアップ
    3. 2. Contextの作成
    4. 3. コンポーネントの実装
    5. 4. アプリケーションの統合
    6. 5. 実行
    7. まとめ
  6. コンポーネント構成とContextの適用
    1. Contextを用いたコンポーネントの分割
    2. 1. コンポーネント階層
    3. 2. Providerの適用
    4. 3. TodoInputでのContext使用
    5. 4. TodoListでのContext使用
    6. 5. TodoItemでのContext使用
    7. 6. コンポーネント間での連携
    8. まとめ
  7. パフォーマンス最適化のポイント
    1. Context API利用時のパフォーマンス課題
    2. 1. Providerの分割
    3. 2. メモ化の活用
    4. 3. コンポーネントの分離
    5. 4. 適切なコンテキスト設計
    6. 5. 開発ツールでの検証
    7. まとめ
  8. トラブルシューティング
    1. Context API利用時によくあるエラーと解決策
    2. 1. Providerを忘れる問題
    3. 2. 値のメモ化忘れによる不要な再レンダリング
    4. 3. 複数のContextの混同
    5. 4. Providerの多重ネスト問題
    6. 5. コンテキスト値の初期化不足
    7. 6. パフォーマンスの低下
    8. まとめ
  9. まとめ