🍒

useContext(): 基本操作とuseReducerを使ったコンテキスト作成

2024/07/21に公開2

React v16.8.0 以降で導入された useContext は、React のフックの一つで、コンポーネントツリー全体にデータを渡すためのもの。useContext を使用すると、コンテキストを直接利用できるため、深くネストされたコンポーネントでも簡単にデータを取得できる。以下に、useContext の含めた作成手順は以下の通り。

1. コンテキストの作成

コンテキストを作成には React.createContext を使用する。使用には、Provider コンポーネントが必要で、value プロパティを通じてコンテキストの値を渡す。

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

const CounterContext = createContext();

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

  const increment = () => setCount(count + 1);
  const decrement = () => setCount(count - 1);

  return (
    <CounterContext.Provider value={{ count, increment, decrement }}>
      {children}
    </CounterContext.Provider>
  );
};
// CounterContextは、コンポーネントから利用する際、
// useContext を使ってこのコンテキストにアクセスする
export { CounterContext, CounterProvider };

2. コンテキストの提供

特定のコンポーネントツリー(またはアプリケーション)にコンテキストの値や関数を渡すための設定方法は以下の通り。

AppEntry.js
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import { CounterProvider } from './CounterContext';

ReactDOM.render(
  <CounterProvider>
    <App />
  </CounterProvider>,
  document.getElementById('root')
);
  • コンテキストの作成と提供の部分を一つのファイルにまとめることも可能。

3. コンテキストの利用

コンテキストの値や関数を利用するには、useContext フックを使用してその値や関数を取得する。

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

const CounterComponent = () => {
  const { count, increment, decrement } = useContext(CounterContext);

  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={increment}>Increment</button>
      <button onClick={decrement}>Decrement</button>
    </div>
  );
};

export default CounterComponent;

4. リデューサーを使ったコンテキストの作成

useReducer フックを使用して複雑な状態管理を行う。リデューサーを使うことで、状態遷移を一つの関数(リデューサー関数)に集約し、状態更新のロジックをより明確かつ管理しやすくする。

手順

  1. 初期状態の定義:状態の初期値を設定する。
  2. アクションの定義:状態をどのように変更するかを指示するアクションを定義する。
  3. リデューサー関数の作成:現在の状態とアクションを受け取り、新しい状態を返す関数を作成する。
  4. コンテキストとプロバイダーの作成:createContext を使用してコンテキストを作成し、useReducer を使って状態管理を行うプロバイダーコンポーネントを作成する。

この方法により、状態の変更が明確に定義され、コンポーネント間での状態管理が容易になる。例えば、複数のアクションがある場合でも、リデューサー関数内でそのロジックを一元管理できる。

useReducer() についての詳細は、こちらの記事をご覧ください:「useReducer(): 基本操作と実装例

MainEntry.js

import React, { useReducer, useCallback, createContext, useContext } from 'react';

const initialState = {
  isStarted: false,
};

const startAction = {
  type: 'START',
};

// リデューサー: stateとactionを受け取り、新しい状態を返す状態管理のための関数。
// state: 現在の状態を保持するオブジェクト。
// action: 状態をどのように変更するかを指示するオブジェクト。
const reducer = (state, action) => {
  switch (action.type) {
    case 'START':
      return {
        ...state,
        isStarted: true,
      };
    default:
      return state;
  }
};
//コンポーネントから利用する際、useContext を使ってこのコンテキストにアクセスする
export const MyContext = createContext();

const MyContextProvider = ({ children }) => {
 // 状態を更新するための関数は、Providerの中に書く
  const [state, dispatch] = useReducer(reducer, initialState);
  const start = useCallback(() => dispatch(startAction), []);

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

export default MyContextProvider;
SomeComponent.js
// コンテキストを利用するコンポーネント例
const SomeComponent = () => {
  const { start, isStarted } = useContext(MyContext);

  return (
    <div>
      <button onClick={start}>Start</button>
      {isStarted && <p>Started!</p>}
    </div>
  );
};

Discussion

nap5nap5

深くネストされたコンポーネントでも簡単にデータを取得できる

ここの部分のデモにチャレンジしてみました。

kkoislandkkoisland

すばらしいです!リンクを貼っていただきとても嬉しいです!Safariではデモが起動しなかったのですが、Chromeに切り替えたらみることができました。ありがとうございます😃