React: Context API とは「公共の掲示板」のようなもの

TwitterFacebookHatena

TL;DR

このページでは、Next.js と TypeScript を用いて Context API の利用法について解説します。このテクニックを使うことで、アプリケーション全体でのデータの流れを効率化できます。

開発環境 バージョン
Next.js 13.4.3
TypeScript 5.0.4
Emotion 11.11.0
React 18.2.0

Context API とは「公共の掲示板」のようなもの

Context API とは、React が提供するグローバルな状態管理のための仕組みです。つまり、アプリケーション全体で共有したいデータを管理することができます。例えば、ユーザー情報や設定値など、複数のコンポーネント間で使用されるデータを Context API を使って管理できます。

Context API は、実は"公共の掲示板"のようなものだと考えると分かりやすいです。

例えば、小学校の掲示板を想像してみてください。掲示板には様々な情報が掲示されていますよね。学校全体のニュースやお知らせ、それから各クラスからのメッセージなど、全ての情報が一箇所に集まっている場所が、この掲示板です。

全ての生徒たちは、この掲示板に行けば自分たちに関係する情報を取得することができます。つまり、重要な情報が一箇所に集まっているため、それを必要とする人たちが容易にアクセスできるのです。

Context API は、この掲示板のようなものと考えることができます。ある特定の情報(状態)を、アプリケーション全体でアクセス可能にすることができます。例えば、ユーザーのログイン情報や、選択したテーマの色など、複数のコンポーネントで共有したい情報を Context API で管理することができます。

コンポーネントは、必要な情報を取得するためにこの"掲示板"(Context)にアクセスし、そこから必要な情報を読み取ります。また、情報を更新することもできます。

つまり、Context API はアプリケーション全体で共有したい情報を一箇所にまとめ、それを必要とするコンポーネントが容易にアクセスできるようにする機能なのです。これにより、様々なコンポーネントで一貫した情報を共有し、同期することが可能になります。

Context API の基本的な利用法を示す、超シンプルなコード例を書いてみます。Context を作成し、それを利用してテーマカラーを管理してみましょう。まずは、Context を作成します。

import { createContext } from 'react'

type ThemeContextType = {
  themeColor: string
  setThemeColor: (value: string) => void
}

// 初期値を設定して、Contextを作成します。
const ThemeContext = createContext<ThemeContextType>({
  themeColor: 'light',
  setThemeColor: () => {},
})

export default ThemeContext

次に、この Context を使ってテーマカラーを管理するコンポーネントを作成します。

import { useState } from 'react'
import ThemeContext from './ThemeContext'

const ThemeProvider: React.FunctionComponent = ({ children }) => {
  const [themeColor, setThemeColor] = useState('light')

  return <ThemeContext.Provider value={{ themeColor, setThemeColor }}>{children}</ThemeContext.Provider>
}

export default ThemeProvider

最後に、この ThemeProvider コンポーネントを利用して、アプリケーション全体でテーマカラーを管理します。

import ThemeProvider from './ThemeProvider'
import ChildComponent from './ChildComponent'

const App = () => {
  return (
    <ThemeProvider>
      <ChildComponent />
    </ThemeProvider>
  )
}

export default App

この例では、ThemeProviderコンポーネントが全体のテーマカラーを管理し、それを Context を通して子コンポーネントに提供しています。そして、ChildComponentコンポーネントはその情報を利用して何かを表示することができます。

このように Context API を利用することで、アプリケーション全体で共有する情報を簡単に管理することができます。

Next.js で、Context API を実装

具体的には、以下のようなソースコードで Context API を使用することが可能です。まず、/contexts/AppContext.tsx ファイルを作成し、AppContext という Context を定義します。

// /contexts/AppContext.tsx
import { createContext, useContext } from 'react'

type AppContextType = {
  username: string
  setUsername: (value: string) => void
}

const AppContext = createContext<AppContextType | undefined>(undefined)

export const useAppContext = () => {
  const context = useContext(AppContext)
  if (context === undefined) {
    throw new Error('useAppContext must be used within a AppProvider')
  }
  return context
}

export const AppProvider: React.Provider<AppContextType> = AppContext.Provider

次に、/_app.tsx で AppProvider を使用します。

// /_app.tsx
import { AppProps } from 'next/app'
import { useState } from 'react'
import { AppProvider } from '../contexts/AppContext'

function MyApp({ Component, pageProps }: AppProps) {
  const [username, setUsername] = useState('Guest')

  return (
    <AppProvider value={{ username, setUsername }}>
      <Component {...pageProps} />
    </AppProvider>
  )
}

export default MyApp

そして、必要なコンポーネントで useAppContext フックを使って Context の値にアクセスできます。

// /pages/index.tsx
import { useAppContext } from '../contexts/AppContext'

const HomePage = () => {
  const { username, setUsername } = useAppContext()

  return (
    <div>
      <p>{username}</p>
      <button onClick={() => setUsername('User1')}>Change username</button>
    </div>
  )
}

export default HomePage

この例では、AppContext という Context を作成し、username と setUsername を保持しています。これらの値はアプリケーション全体でアクセス可能となります。

Context API の詳細な使い方

Context API は比較的シンプルな方法でグローバルな状態管理を行うことができますが、大規模なアプリケーションではより詳細な実装が必要になることもあります。例えば、コンテキストの値を操作するための特定のアクションを定義したり、複数のコンテキストを一つにまとめたりすることがあります。

Emotion で実装

Emotion を用いて、スタイルをコンテキストとして管理することも可能です。例えば、テーマ情報(色、フォントサイズ等)をコンテキストとして管理し、アプリケーション全体でその情報を共有することができます。そのためのソースコードは以下の通りです。

// /contexts/ThemeContext.tsx
import { createContext, useContext } from 'react'
import { css } from '@emotion/react'

type ThemeType = {
  primaryColor: string
  secondaryColor: string
  spacing: (size: number) => string
}

const ThemeContext = createContext<ThemeType | undefined>(undefined)

export const useThemeContext = () => {
  const context = useContext(ThemeContext)
  if (context === undefined) {
    throw new Error('useThemeContext must be used within a ThemeProvider')
  }
  return context
}

export const ThemeProvider: React.Provider<ThemeType> = ThemeContext.Provider

そして、テーマ情報を使用したいコンポーネントでは以下のように書けます。

// /components/ThemedButton.tsx
import { useThemeContext } from '../contexts/ThemeContext'

const ThemedButton = () => {
  const { primaryColor, spacing } = useThemeContext()

  return (
    <button
      css={css`
        background-color: ${primaryColor};
        padding: ${spacing(2)};
      `}
    >
      Click me
    </button>
  )
}

export default ThemedButton

このようにして、Emotion と Context API を組み合わせることで、アプリケーション全体でのスタイル管理をより一貫性を持って行うことが可能になります。ユーザーが選んだテーマに応じてスタイルを動的に変更するなど、さまざまな応用が考えられますよね。

以上が Next.js と TypeScript を使って Context API を活用する方法の一例です。今回の例では、アプリケーションのユーザー名とテーマをグローバルに管理する方法を紹介しましたが、実際にはもっと様々な情報を管理することが可能です。Context API の特徴的な利点は、そのシンプルさと直感的な使い方にあります。これにより、大きな学習コストを必要とせずに、アプリケーションの状態管理を行うことができるんですよね。

React: Context API とは「公共の掲示板」のようなもの