今回は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の凄さを体感できる良い例ですね。