目次
はじめに
今回もまた React の hooks について書いてきたいと思います。
useReducer の時と同様に、Udemy で勉強させていただいている好きな講座のアウトプットとしての記事となりそうですが…。
今回は useContext について取り上げたいと思います!
useContext とは?
まず、useContext
とは React の hooks のひとつで、props や state をグローバルに共有することができるものです。
親コンポーネントで定義した値を子孫コンポーネントで使いたい場合、親コンポーネント=>子コンポーネント=>子孫コンポーネント?といった感じで props のバケツリレーをすると非常に冗長になってしまうところを、useContext
を使うことでシンプルにまとめることができるようになります!
具体的に役立つ場面
useContext
を使わない場合だと、こんな感じでバケツリレーすることになります。
親コンポーネントのParent.jsx
import Child from '../lower/Child'
const Parent = () => {
const props = 'propsValue'
return <Child props={props} />
}
export default Parent
子コンポーネントのChild.jsx
import GrandChild from './GrandChild'
const Child = ({ props }) => {
return <GrandChild props={props} />
}
export default Child
孫コンポーネントのGrandChild.jsx
const GrandChild = ({ props }) => {
// 'propsValue'が表示される
return <p>{props}</p>
}
export default GrandChild
props を少し深い階層で渡したいだけなのに、毎回こんな処理を行うのは面倒ですよね…。
コードがあまり気持ちよくないですね。
なので、最初に props を定義しているParent.jsx
で useContext
を使ってバケツリレーをせずにGrandChild.jsx
で props を呼び出してみましょう。
具体的な使い方
それでは実際にuseContext
を使った場合の具体的な使い方を見ていきましょう!
1.値を定義するコンポーネント内でcreateContext
をreact
から読み込みます
import { createContext } from 'react'
import Child from '../lower/Child'
const Parent = () => {
return <Child props={props} />
}
export default Parent
2.context を定義します
createContext
の引数に、これまで props で渡していた値を渡します。
import { createContext } from 'react'
import Child from '../lower/Child'
// 他のコンポーネントで呼び出すのでexportしておきましょう
export const ContextValue = createContext(
// 他のコンポーネントに渡したい値を入れる
'propsValue'
)
const Parent = () => {
return <Child props={props} />
}
export default Parent
3.context を使用したいコンポーネントで読み込みます
孫コンポーネントのGrandChild.jsx
で値を呼び出してみましょう。
内容としては、useContext
の引数にParent.jsx
で定義したContextValue
を渡すことでその値を取得することができるようになります。
import { useContext } from 'react'
import { ContextValue } from '../../Parent'
const GrandChild = () => {
const propsValue = useContext(ContextValue)
return (
// 'propsValue'が表示される
<p>{propsValue}</p>
)
}
export default GrandChild
このように useContext を使って処理をすることで、props のバケツリレーをする必要がなくなり、Child.jsx
にムダな処理を書く必要がなくなって面倒くささが解消された気がしますね!
state の受け渡し
上記では props の受け渡しの例を見てみましたが、実際の案件のなかでは state もグローバルで管理したい場面も当然出てくると思うので、その実装方法も解説していきます。
今回の場合はGrandChild.jsx
で値を更新して、Child.jsx
で値を取得してみます!
1.状態管理をおこなう親コンポーネントを Provider で囲む
state を管理する時には先ほどとは大きく違う点があって、createContext
で定義した値のProvider
でそれぞれのコンポーネントを囲む必要があります。
そして、その中のvalue
で設定された値が useContext
を通して取得できるようになります。
今回は state を取得したいので、value
にはuseState
で定義しておいた値を渡しておきましょう。
import { createContext, useState } from 'react'
import Child from '../lower/Child'
import GrandChild from '../lower/GrandChild'
export const ContextValue = createContext()
const Parent = () => {
const [state, setState] = useState()
return (
<ContextValue.Provider value={[state, setState]}>
<Child />
<GrandChild />
</ContextValue.Provider>
)
}
export default Parent
2.状態を更新する
先ほどのParent.jsx
のContextValue.Provider
のvalue
に渡した値を呼び出して使います。
今回のGrandChild.jsx
では状態の更新のみをおこなうので、value
に渡された配列の 1 番目の値であるsetState
を使えるようにしておきます。
import { useContext } from 'react'
import { ContextValue } from '../../Parent'
const GrandChild = () => {
// 更新用の値のみ呼び出す
const [, setState] = useContext(ContextValue)
// 値の更新
const handleCoutUp = () => {
setState((prev) => prev + 1)
}
return <button onClick={handleCoutUp}>count up</button>
}
export default GrandChild
3.状態を取得する
更新時と同様にuseContext
を使って state を呼び出していますが、今回のChild.jsx
では状態の取得のみをおこなうので、value
に渡された配列の 0 番目の値であるstate
を使えるようにしておきます。
import { useContext } from 'react'
import { ContextValue } from '../../Parent'
const Child = () => {
// 表示用の値のみ呼び出す
const [state] = useContext(ContextValue)
// 値の表示
return <p>現在のカウント: {state}</p>
}
export default Child
context を分離する
上記でも問題ない挙動にはなりますが、状態の管理を別で持たせておくことでコンポーネントごとの役割を明確にして再利用性も高めることができると思うので、ここでコードの整理をしてみましょう。
1.状態の管理のみをおこなうStateContext.jsx
を作成する
先ほどContextValue
を定義していたParent.jsx
から切り離して、この中で処理をまとめておきます。
import { useState } from 'react'
export const ContextValue = createContext()
export const StateContext = ({ children }) => {
const [state, setState] = useState()
return (
<ContextValue.Provider value={[state, setState]}>
{children}
</ContextValue.Provider>
)
}
2.Parent.jsx
で使用してみる
StateContext.jsx
を使うまでは、createContext
やuseState
を定義したりでごちゃごちゃしていましたが、それらの処理はStateContext
にまとめておいたので非常にシンプルに整理することができました。
import { StateContext } from '../context/StateContext'
import Child from '../lower/Child'
import GrandChild from '../lower/GrandChild'
const Parent = () => {
return (
<StateContext>
<Child />
<GrandChild />
</StateContext>
)
}
export default Parent
3.更新・取得もまとめる
今のままでは更新・取得をおこなう際にuseContext
とContextValue
の両方を呼び出して使う必要があるので、こちらもStateContext.jsx
でまとめておきましょう!
import { useState, useContext } from 'react'
export const ContextValue = createContext()
export const StateContext = ({ children }) => {
const [state, setState] = useState()
return (
<ContextValue.Provider value={[state, setState]}>
{children}
</ContextValue.Provider>
)
}
// ここでuseContextを使ってProviderのvalueを呼び出せるようにしておく
export const useValue = () => useContext(StateContext)
更新する方はこう
import { useValue } from '../context/StateContext'
const GrandChild = () => {
// 更新用の値のみ呼び出す(useContext(ContextValue)からuseValue()に書き換える)
const [, setState] = useValue()
// 値の更新
const handleCoutUp = () => {
setState((prev) => prev + 1)
}
return <button onClick={handleCoutUp}>count up</button>
}
export default GrandChild
取得する方はこう
import { useValue } from '../context/StateContext'
const Child = () => {
// 表示用の値のみ呼び出す(useContext(ContextValue)からuseValue()に書き換える)
const [state] = useValue()
// 値の表示
return <p>現在のカウント: {state}</p>
}
export default Child
先ほどと比較して、だいぶすっきりになったコードになってみやすくなりましたね。
参考
- https://beta.reactjs.org/reference/react/useContext
- https://www.udemy.com/share/106Nqw3@17wneCmC7u77GFgtqR132aAAZBpQECHRhGEFJPRI-8wDQQTLHpX4sATtcd_IZof8ww==/
- https://dev.classmethod.jp/articles/react-i-checked-again-how-to-use-usecontext/
さいごに
これまでグローバルな状態管理をしたことがなくはなかったのですが、ライブラリの言う通りにでなんとなく実装していたところがあったので、こうして React の hooks として用意されている useContext
の使い方をちゃんと学ぶことが理解が深まった気がするのでよかったです。
やはり基礎の学び直しは大切ですね。