[React] useReducer + useContext

今回はReactのHookを2つ組み合わせてプログラムを組んでみます。親・子・孫の3つのコンポーネントを作り親と孫で同じ状態をキープするプログラムです。具体的には親と孫でそれぞれにボタン(加算・減算・リセット)を作りカウント数を共有してみます。

作成するプログラムの出力イメージ

親のボタン、孫のボタンどちらを押しても、親と孫に表示されているカウント数(現在値)が同時に変化します。

useContext設定ファイル

まず「src/contexts」ディレクトリにAppContext.jsファイルを作ります。useContextを使うための基本的な記述をします。createContextをインポートし、contextをAppContextという変数に入れます。それを他で利用できるようにエクスポートしています。

import {createContext} from 'react'
 
const AppContext = createContext()
export default AppContext

App.js(親)コンポーネント

次に親を作成します。デフォルトで用意されているApp.jsに下記のプラグラムを記述します。

AppContextとuseRecucerそしてBモジュールをインポートします。

import './App.css';
import AppContext from './contexts/AppContext';
import B from './components/B';
import { useReducer } from 'react'

const initialState = 0
const reducer = (prev, action) => {
  switch (action) {
    case 'plus':
      return prev + 1
    case 'minus':
      return prev - 1
    case 'reset':
      return 0
    default:
      return prev
  }
}

function App() {
  const [count, dispatch] = useReducer(reducer, initialState)
  return (
    <div className="App">
          <h3>A</h3> 
      <p>カウント数: {count}</p>
      <AppContext.Provider value={{ countContext: count, dispatchContext: dispatch }} >

        <button onClick={() => dispatch('plus')}>プラス</button>
        <button onClick={() => dispatch('minus')}>マイナス</button>
        <B />
      </AppContext.Provider>
    </div>
  );
}

export default App;

useReducerの初期値を0、そしてreducerの処理を作ります。

function App() {}の中にuseReducerの式を記載します。

重要なのは

<AppContext.Provider value={{ countContext: count, dispatchContext: dispatch }} ></AppContext.Provider>

で囲まれた中でuseContextが使えるということです。この中に<B />が記載されているので、Bに含まれるCモジュール(孫)でもuseContextが使えるということになります。

useContext , useRecucerの詳しい説明は下記のリンクを参考にして下さい。

B(子)モジュール

BコンポーネントはCコンポーネントを読み込んでいるだけです。

表示したときにABCの区別が付きやすいように<hr>区切り線を入れています。

import C from './C';

import React from 'react'

const B = () => {
  return (
    <div>
        <hr></hr>
        <h3>B</h3>
        <hr></hr>
      <C />
    </div>
  )
}

export default B

C(孫)モジュール

まず、useContext , AppContextをインポートしておきます。この2つは記述方法が違うので注意が必要です。

import React from 'react'
import {useContext} from 'react'
import AppContext from '../contexts/AppContext'

const C = () => {
    const{countContext ,dispatchContext} = useContext(AppContext)
  return (
    <div>
         <h3>C(孫)</h3> 
         <button onClick ={()=>dispatchContext('plus')}>プラス</button>
         <button onClick ={()=>dispatchContext('plus')}>マイナス</button>
         <button onClick ={()=>dispatchContext('reset')}>リセット</button>
         現在値 : {countContext}
    </div>
  )
}

export default C

親コンポーネント内でuseContextで使う変数を2つ定義しました。 

<AppContext.Provider value={{ countContext: count, dispatchContext: dispatch }} >

  • countContext: count
  • dispatchContext: dispatch

この一行でuseContextの値をCモジュールモジュールで使えるようにしています。const{countContext ,dispatchContext} = useContext(AppContext)

あとはボタンや表示にこれらの変数を使えば良いだけです。

「ボタン」

<button onClick ={()=>dispatchContext(‘plus’)}>プラス</button>

「カウンター数の表示」

現在値 : {countContext}

親・子・孫で同時に値を変更

これまでは親(APP)と孫(C)だけでステートを共有していましたが、今度は子(B)でも共有できるようにします。APP・B・Cそれぞれにボタンを設置し、カウント数をそれぞれで共有してみます。

上記のプログラムができていればとても簡単。コピペで動かせるレベルです。変更するのはBだけでOKです。

import C from './C';
import {useContext} from 'react'
import AppContext from '../contexts/AppContext'

import React from 'react'

const B = () => {
  const{countContext ,dispatchContext} = useContext(AppContext)
  return (
    <div>
        <hr></hr>
        <h3>B(子)</h3>
        <button onClick ={()=>dispatchContext('plus')}>プラス</button>
         <button onClick ={()=>dispatchContext('plus')}>マイナス</button>
         <button onClick ={()=>dispatchContext('reset')}>リセット</button>
         現在値 : {countContext}
        <hr></hr>
      <C />
    </div>
  )
}

export default B

これで3つのコンポーネントで値を共有できるようになりました。

useRecucerとuseContextを使うとバケツリレーが無くなるのでとても良いですね。jQuery時代に作ったプログラムはバケツリレーだらけで途中で嫌になりました。useRecucerとuseContextの組み合わせ技はReactHooksの凄さを体感できる良い例ですね。



Author: webmaster