引用元
※引用元がすごくわかりやすかったので、そこから改変せず抜粋して記事を作成しています
https://weseek.co.jp/tech/2565/
Global stateの管理が必要な理由
React において、例えばコンポーネントの構成が5階層になっていた場合に、一番上の階層のコンポーネントで定義している state を一番下の階層のコンポーネントに渡したい場合はどうすれば良いでしょうか。
上から下までのそれぞれのコンポーネントで state を props として渡してあげることで、一番下の階層のコンポーネントまで渡すことができます。
しかし、この方法では state をバケツリレーのように複数のコンポーネントにまたがって渡しているため、コードが複雑化してしまいます。また、中間のコンポーネントにとっては本来必要のない props を受け取っているため、再利用の難しいコンポーネントになってしまう可能性があります。
その他にも、中間に位置するコンポーネント達は state が更新されるたびに、再レンダリングの必要がないにもかかわらず再レンダリングされてしまうという問題があります。
これらの問題を解決し、無駄なコードや無駄な挙動を減らすために Global state が必要になるということです。
React Context
一番初めに紹介するのは、React の標準機能として備わっている useContext を使用した方法です。
この方法は React さえ導入されていれば使用できるため、追加で他のパッケージをインストールする必要がないことがメリットです。
まず初めに、ページ全体のテーマカラーを Global state として扱うことを想定して、一番上の Top コンポーネントで定義されたテーマカラーを一番下の Bottom コンポーネントに表示させるという例を用意します。
import { useState } from 'react'
const Top = () => {
const [themeColor] = useState('dark')
return (
<Middle color={themeColor} />
)
}
const Middle = (props) => {
return (
<Bottom color={props.color} />
)
}
const Bottom = (props) => {
return (
<div>テーマカラーは {props.color} です。</div>
)
}
中間の Middle コンポーネントでは、次の Bottom コンポーネントに props を渡すためだけに props を受け取っていることが分かります。
(この程度であれば全然問題はないですが) もし state の渡し先がずっと下層のコンポーネントであった場合は複雑なコードとなってしまいます。
この例を元に useContext を使用して、Top コンポーネントから Bottom コンポーネントに直接 state を渡せるように改善します。
React の context 機能を使用した Global state 管理の手順は非常にシンプルで、以下の4ステップで実施できます。
- React.createContext で Context オブジェクトを作成する
- Context.Provider の value に対して state を渡す
- state の渡し先のコンポーネントを Context.Provider で囲う
- 渡し先のコンポーネントで React.useContext を使用して state を呼び出す
例を見ていきましょう。
import { useState, createContext, useContext } from 'react'
const SampleContext = createContext('light')
const Top = () => {
const [themeColor] = useState('dark')
return (
<SampleContext.Provider value={themeColor}>
<Middle />
</SampleContext.Provider>
)
}
const Middle = () => {
return (
<Bottom />
)
}
const Bottom = () => {
const color = useContext(SampleContext)
return (
<div>テーマカラーは {color} です。</div>
)
}
まず、createContext で Context オブジェクトを作成します。
createContext の引数には Global state の初期値を設定できます。今回は light とします。
const SampleContext = createContext('light')
次に、Context.Provider の value に、Global state として管理する state を渡します。
そして、Context.Provider で Middle コンポーネントを囲います。
こうすることにより、Middle コンポーネントとその配下のコンポーネントで、useContext の使用が可能になります。
const Top = () => {
const [themeColor] = useState('dark')
return (
<SampleContext.Provider value={themeColor}>
<Middle />
</SampleContext.Provider>
)
}
最後に、createContext で作成した Context を useContext の引数に渡してあげることで Global state を呼び出すことができます。
const Middle = () => {
return (
<Bottom />
)
}
const Bottom = () => {
const color = useContext(SampleContext)
return (
<div>テーマカラーは {color} です。</div>
)
}
これにより、中間コンポーネントがいくつ存在していようと、直接最下層のコンポーネントで state を扱えるようになりました。
SampleContext.Provider を使用しているコンポーネント以外の箇所で Global state を更新したい場合は、useState を使用し state とそれを更新するための関数の両方を Context.Provider の value に渡してあげることで更新が可能となります。
以下の例では useState を使用したカスタムフックを作成しています。
import { useState } from 'react'
const useThemeColor = () => {
const [themeColor, setThemeColor] = useState('light')
return {
themeColor,
setThemeColor
}
}
export default useThemeColor
import { createContext, useContext } from 'react'
import useThemeColor from './hooks/useThemeColor'
const SampleContext = createContext()
const Top = () => {
return (
<SampleContext.Provider value={useThemeColor()}>
<Middle />
</SampleContext.Provider>
)
}
const Middle = () => {
return (
<Bottom />
)
}
const Bottom = () => {
const context = useContext(SampleContext)
return (
// ボタンを押した時に context.themeColor を 'dark' に更新する
<button onClick={() => context.setThemeColor('dark')}>テーマカラー更新</button>
)
}
Global state が更新されるたびに、useContext でその Context を参照している全てのコンポーネントが再レンダリングされるため、1つの Context で全ての Global state を管理するのではなく、必要に応じて複数の Context を用意すると良いでしょう。
その他、React Context については React の公式ドキュメントに詳しく書かれているため、気になる方は是非ご覧ください。
コンテキストのガイド
この React Context を使った Global state の管理は、今回紹介する方法の中で比較的お手軽に使うことができるので、小規模なプロジェクトではこの方法を用いるのが良いのではないかと思われます。