3分でわかるReactのContextによるグローバルStateの管理方法

ContextによるグローバルState

Reactで親コンポーネント => 子コンポーネント => 孫コンポーネント の順でpropsを使用すると管理しづらくなるため、ContextによるグローバルStateを使うことがある。

この記事ではContextによるグローバルStateの管理方法について簡単に解説しています。

Viteで前準備

まず、以下のコマンドでVite + React環境を準備します。

npm init vite@latest

プロジェクト名などは以下の通り。

✔ Project name: … vite-react
✔ Select a framework: › React
✔ Select a variant: › JavaScript

準備が完了したらまずmain.jsxを以下に書き換えます。

import ReactDOM from 'react-dom/client'
import App from './App'

ReactDOM.createRoot(document.getElementById('root')).render(
  <App />
)

次にApp.jsxを以下に書き換えます。

buttonのonClickでContent.jsx => Button.jsxのbuttonのdisabledのfalse, trueが切り替わる処理を書きます。

import { useState } from 'react'
import { Content } from './components/Content'

function App() {
  const [isClick, setClick] = useState(false)
  const onClickToggle = () => setClick(!isClick)

  return (
    <>
      <button onClick={onClickToggle}>buttonを有効・無効</button>
      <Content isClick={isClick}></Content>
    </>
  )
}

export default App

次にsrc/compornents/Content.jsxを作成します。

import { Button } from './Button'

export const Content = props => {
  const { isClick } = props

  return (
    <>
      <Button isClick={isClick} />
    </>
  )
}

最後にsrc/compornents/Button.jsxを作成します。

export const Button = props => {
  const { isClick } = props

  return (
    <button disabled={!isClick}>button</button>
  )
}

これでnpm run dev (npm run build)を実行すると以下のようなSPAが作成されます。

Reactでpropsを使用したサンプル

これでも動作するが、親 (App) => 子 (Content) => 孫 (Button)の過程でpropsが複数使用されているので、グローバルStateの管理に変更する。

providersを作成

src/component/providers/ButtonProvider.jsxを作成してcreateContextとuseStateをimportして使用するコードを作成する。

import { createContext, useState } from 'react'

export const ButtonContext = createContext({})
export const ButtonProvider = (props) => {
  const { children } = props
  const [isClick, setClick] = useState(false)

  return (
    <ButtonContext.Provider value={{isClick, setClick}}>
      {children}
    </ButtonContext.Provider>
  )
}

次にmain.jsxでButtonProviderをimportして<App />を囲む。

これで<App />内は何階層でもpropsではなくContextによるグローバルStateから値を受け取れる。

import ReactDOM from 'react-dom/client'
import App from './App'
import { ButtonProvider } from './components/providers/ButtonProvider'

ReactDOM.createRoot(document.getElementById('root')).render(
  <ButtonProvider>
    <App />
  </ButtonProvider>
)

次にApp.jsxのuseStateをuseContextに書き換えて、ButtonContextをimportしてuseContextでisClickとsetClickに代入する。

代入時のconst部分が[]ではなく{}になっているので注意。

const [isClick, setClick] = useState(false)
↓
const {isClick, setClick} = useContext(ButtonContext)
import { useContext } from 'react'
import { ButtonContext } from './components/providers/ButtonProvider'
import { Content } from './components/Content'

function App() {
  const {isClick, setClick} = useContext(ButtonContext)
  const onClickToggle = () => setClick(!isClick)

  return (
    <>
      <button onClick={onClickToggle}>buttonを有効・無効</button>
      <Content isClick={isClick}></Content>
    </>
  )
}

export default App

次にpropsでContent.jsxからButton.jsxにpropsで渡す必要がなくなったのでContent.jsxのpropsを削除する。

import { Button } from './Button'

export const Content = () => {

  return (
    <>
      <Button />
    </>
  )
}

最後にButton.jsxでButtonContextをimportしてconst { isClick } = useContext(ButtonContext)で代入すれば完成です。

import { useContext } from 'react'
import { ButtonContext } from './providers/ButtonProvider'

export const Button = () => {
  const { isClick } = useContext(ButtonContext)

  return (
    <button disabled={!isClick}>button</button>
  )
}

ReactでContextによるグローバルStateの管理を使用したサンプル