ReactのuseContext入門:コンポーネント間で状態を共有する

スポンサーリンク

ReactのuseContext入門:コンポーネント間で状態を共有する

useContextとは

useContext はコンポーネントツリーを通じて値を共有するためのフックです。propsのバケツリレー(中間のコンポーネントを経由してpropsを渡し続ける問題)を解決できます。


propsのバケツリレーとは

// 問題:Buttonに渡したいだけなのにMiddleでも受け取る必要がある
function App() {
  const [theme, setTheme] = useState('dark')
  return <Middle theme={theme} />
}

function Middle({ theme }: { theme: string }) {
  return <Button theme={theme} /> // 自分では使わないのに渡している
}

function Button({ theme }: { theme: string }) {
  return <button className={theme}>Click</button>
}

Contextで解決する

1. Contextを作成する

import { createContext, useContext, useState } from 'react'

type ThemeContextType = {
  theme: string
  setTheme: (theme: string) => void
}

const ThemeContext = createContext<ThemeContextType | null>(null)

2. Providerで値を提供する

function App() {
  const [theme, setTheme] = useState('dark')

  return (
    <ThemeContext.Provider value={{ theme, setTheme }}>
      <Middle />
    </ThemeContext.Provider>
  )
}

3. useContextで値を取得する

function Button() {
  const ctx = useContext(ThemeContext)
  if (!ctx) return null

  return (
    <button
      className={ctx.theme}
      onClick={() => ctx.setTheme('light')}
    >
      テーマ切替
    </button>
  )
}

中間の Middle はpropsを一切受け取らずに済みます。


カスタムフックで使いやすくする

useContext を毎回 null チェックするのは面倒なので、カスタムフックにまとめるのが一般的です。

function useTheme() {
  const ctx = useContext(ThemeContext)
  if (!ctx) throw new Error('useTheme must be used within ThemeProvider')
  return ctx
}

// 呼び出し側がシンプルになる
function Button() {
  const { theme, setTheme } = useTheme()
  return <button className={theme}>Click</button>
}

Providerを分離してコンポーネントを整理する

function ThemeProvider({ children }: { children: React.ReactNode }) {
  const [theme, setTheme] = useState('dark')
  return (
    <ThemeContext.Provider value={{ theme, setTheme }}>
      {children}
    </ThemeContext.Provider>
  )
}

// App がすっきりする
function App() {
  return (
    <ThemeProvider>
      <Middle />
    </ThemeProvider>
  )
}

useContextで管理するのが向いているもの

向いている 向いていない
テーマ(ダーク/ライト) 頻繁に更新される状態
ログインユーザー情報 複雑な非同期処理
言語設定(i18n) 大規模なグローバル状態
モーダルの開閉状態

Contextは再レンダリングの最適化が難しいため、更新頻度が高い状態や大規模なアプリには Redux Toolkit や TanStack Query が向いています。


まとめ

手順 コード
Contextを作成 createContext<型>(初期値)
値を提供 <Context.Provider value={...}>
値を取得 useContext(Context)
  • useContext は小〜中規模のグローバル状態共有に適している
  • カスタムフック化するとコードが整理される
  • 大規模・高頻度更新は Redux Toolkit を検討する

Reactのプロジェクト作成から始める場合は「React + Vite でプロジェクト作成」を参照してください。

状態管理の基本は「ReactのuseState入門」を、複雑な状態遷移には「ReactのuseReducer入門」を、アプリ全体のグローバル状態管理には「Redux Toolkit 入門」を参照してください。