連載目次 : React入門
前回の記事 : useReducer / React Hooks
今回はReact Hooksの目玉機能の一つであるuseContext
について解説を行います。
useContext
を利用することで様々なコンポーネントから参照できるステートを作成することができます。
useContextの基本
React.createContext()
を利用することで、ステートの状態管理が可能なProvider
コンポーネントが作成でき、value属性で管理したいステートを指定することができ、Provider内の子孫コンポーネントではuseContext
を利用して管理しているステートにアクセスできます。
import React from 'react';
export const MyContext = React.createContext();
export default function App() {
return (
<MyContext.Provider value="管理したい値">
(Provider内)
</MyContext.Provider>
);
}
もう少し具体的なコードに落とし込んで挙動を確認していきましょう。
以下のサンプルではAppコンポーネント内でSiteContext
という名前でコンテキストの定義をおこない、管理する値は{name: 'to-R Media'}
というオブジェクトにします。
子孫コンポーネントとしてHeaderコンポーネントとBodyを読み込み配置しておきます。
import React from 'react';
import Header from './Header'
import Body from './Body'
export const SiteContext = React.createContext();
export default function App() {
return (
<SiteContext.Provider value={{name: 'to-R Media'}}>
<Header />
<Body />
</SiteContext.Provider>
);
}
子孫コンポーネントではuseContext
を利用して先祖要素にあるプロバイダーが持つ状態を取得できますのでsiteState.name
としてコンポーネントに出力しています。
import React , {useContext} from 'react';
import { SiteContext } from './App';
export default function Header () {
const siteState = useContext(SiteContext);
return <h1>{siteState.name}</h1>
}
import React , {useContext} from 'react';
import { SiteContext } from './App';
export default function Body () {
const siteState = useContext(SiteContext);
return <p>これは「{siteState.name}」の内容です</p>
}
今回は親子関係のコンポーネントのためPropsで引き渡すほうが簡単なのですが、useContext
を利用すれば深い位置に配置された子孫コンポーネントからも先祖のプロバイダーで管理している状態を取得できるので、コンポーネント構造が複雑にそして深くなる場合に力を発揮します。
useReducerとの連携
useContext
単体ではただの状態の参照でグローバル変数等でも代替可能です。
useContext
の真髄はuseReducerと組み合わせて更新可能なステートを作成できる所にあります。
先程のサンプルをuseReducer
でラッピングしてnameを更新できるコンポーネントとしてForm
を追加してみましょう。
src/App.js
にはuseReducer
のstate
とdipatch
を取得できるコンポーネントしてSiteProvider
コンポーネントを新たに作成しています。
import React , {useReducer} from 'react';
import Header from './Header'
import Body from './Body'
import Form from './Form'
const initialState = {
name : 'to-R Media'
}
function reducer(state, action) {
switch (action.type) {
case 'CHANGE_NAME':
return {
...state,
name: action.payload
};
default :
return state
}
}
export const SiteContext = React.createContext();
const SiteProvider = ({children}) => {
const [state, dispatch] = useReducer(reducer, initialState)
return <SiteContext.Provider value={{state, dispatch}}>
{children}
</SiteContext.Provider>
}
export default function App() {
return (
<SiteProvider>
<Header />
<Body />
<Form />
</SiteProvider>
);
}
HeaderコンポーネントとBodyコンポーネントは機能自体は変わりませんが、取得時にstate
から取り出す必要があります。
import React , {useContext} from 'react';
import { SiteContext } from './App';
export default function Header () {
const { state } = useContext(SiteContext);
return <h1>{state.name}</h1>
}
import React , {useContext} from 'react';
import { SiteContext } from './App';
export default function Body () {
const {state} = useContext(SiteContext);
return <p>これは「{state.name}」の内容です</p>
}
新たに作成したFormコンポーネントでは入力値に変更のタイミングでdispach
を実行してSiteContext
の値を更新するようにしています。
import React , {useContext} from 'react';
import { SiteContext } from './App';
export default function Form () {
const { state, dispatch } = useContext(SiteContext);
return <input
type="text"
value={state.name}
onChange={e => dispatch({
type:'CHANGE_NAME',
payload:e.target.value
})}
/>
}
これにより特定のコンポーネントで更新したステートが参照している全コンポーネントに反映されるようになりました。
Reduxのような状態管理ツールを利用している場合には、状態管理はReduxにまかせてしまったほうが良いですが、Reduxなどを利用していない場合にはuseContext
を利用して各コンポーネント内で処理を完結できる仕組みを作ることができます。
次回はReact Hooksのもう一つの目玉機能のカスタムフックについて解説を行っていきます。
連載目次 : React入門
前回の記事 : useReducer / React Hooks
フロントエンドエンジニア積極採用中
株式会社トゥーアールでは現在フロントエンドエンジニア積極的に採用中です!
興味がある人は採用ページをチェック!!