Context Hooks作成の流れ
- 扱いたい値やメソッドをInterfaceで定義する
- createContextでContextを作成する
- 作成したContextのプロバイダーを定義する
- useContextでフックを定義する
扱いたい値やメソッドをInterfaceで定義する
TypeScript利用の場合、 状態管理したいデータのInterfaceを定義しておきます。
// スコアの定義
interface Score {
teamA: number,
teamB: number
}
// チームAとチームBのスコア記録したい
interface ScoreBoardContextType {
score: Score
addPoint: (team: 'teamA'|'teamB', point: number) => void
getResult: () => string
}
AチームとBチームがいるようなスコアボードを想定します。
それぞれの点数を表すScoreと、点数を加算するaddPoint、結果を出力するgetResultを考えます。
createContextでContextを作成する
ReactのcreateContextでまずはContextを定義します。
import React, { createContext } from 'react'
...
// 扱うStateを保持するContextを定義
const ScoreBoardContext = createContext<ScoreBoardContextType>(null)
作成したContextのプロバイダーを定義する
作成したContextのプロバイダーを定義します。
後述しますが、このプロバイダー配下のコンポーネントでは、useContextを使うことでプロバイダー内のStateやメソッドにアクセスすることができます。
import React, { createContext, useState } from 'react'
...
// プロバイダーを定義
export const ScoreBoardProvider = ({ children }: { children: React.ReactNode }) => {
// 各チームのスコアを保持するStateを作成
const [score, setScore] = useState<Score>({
teamA: 0,
teamB: 0
})
// ポイントを加算する
const addPoint = (team: 'teamA'|'teamB', point: number) => {
setScore({
...score,
[team]: score[team] + point
})
}
// 結果を表示する
const getResult = (): string => {
if (score.teamA === score.teamB) {
return `Draw`
} else {
return `${score.teamA > score.teamB ? 'teamA' : 'teamB'} Win!`
}
}
const value = {
score,
addPoint,
getResult
}
// valueにScoreBoardContextTypeの値を渡す
return <ScoreBoardContext.provider value={value}>{children}</ScoreBoardContext.provider>
}
前述のScoreBoardContextTypeで定義した値とメソッド(score, addPoint, getResult)を、
ScoreBoardContext.providerのvalueに渡します。
useContextでフックを定義する
プロバイダーが定義できたら、
その配下でuseContext(ScoreBoaredContext)を呼ぶことで、ScoreBoardProviderのvalueにアクセスすることができます。
毎回useContext(ScoreBoaredContext)と書くのは面倒ですので、useScoreBoardフックを定義することで、簡潔にします。
import React, { createContext, useContext, useState } from 'react'
...
// フックとして定義
const useScoreBoard = () => useContext(ScoreBoardContext)
Context Hooksの使い方
- 作成したプロバイダーで、その値やメソッドを使いたいコンポーネントを囲む
- コンポーネントでContext Hooks(フック)を呼び出す
プロバイダーでコンポーネントを囲む
プロバイダーで囲まれたコンポーネント内ではContext Hooks(フック)が使用可能になります。
import React from 'react'
import { ScoreBoardProvider } from './useScoreBoard'
import { TableTennisGame } from './TableTennisGame'
// プロバイダーで囲む
const App = () => {
<ScoreBoardProvider>
<TableTennisGame />
</ScoreBoardProvider>
}
この例では卓球ゲーム(TableTennisGame)でスコアボードを使いたいので、ScoreBoardProviderで囲みました。
Context Hooks(フック)を呼び出す
プロバイダーで囲まれた配下のコンポーネントでは、フックを呼び出すことができます。
import React from 'react'
import { useScoreBoard } from 'useScoreBoard'
// 卓球ゲーム
export const TableTennisGame = () => {
// プロバイダー配下でContext Hooksを使う
const board = useScoreBoard()
// コンポーネント内で値やメソッドにアクセス可能になる
const pointA = () => board.addPoint('teamA', 1)
const pointB = () => board.addPoint('teamB', 1)
const showResult = () => {
window.alert(board.getResult())
}
return (
<>
<div>teamA: {board.score.teamA} / teamB: {board.score.teamA}</div>
<input type="button" onClick={pointA} value="pointA" />
<input type="button" onClick={pointB} value="pointB" />
)
}
TableTennisGameでuseScoreBoardを呼び出すと、定義しておいたscoreや、addPointなどのメソッドを使用できるようになります。
Context Hooksまとめ
今回の例で作成したuseScoreBoard.tsxは以下の定義のセットになっています。
- ScoreBoardContextType
- ScoreBoardContext
- ScoreBoardProvider
- useScoreBoard
プロバイダーに囲まれた配下のコンポーネントでは、
useScoreBoardを呼び出すことで各値やメソッドを使うことができます。
Context Hooksのサンプル
import React, { createContext, useContext, useState } from 'react'
interface Score {
teamA: number,
teamB: number
}
// チームAとチームBのスコア記録したい
interface ScoreBoardContextType {
score: Score
addPoint: (team: 'teamA'|'teamB', point: number) => void
getResult: () => string
}
// 扱うStateを保持するContextを定義
const ScoreBoardContext = createContext<ScoreBoardContextType>(null)
// プロバイダーを定義
export const ScoreBoardProvider = ({ children }: { children: React.ReactNode }) => {
// 各チームのスコアを保持するStateを作成
const [score, setScore] = useState<Score>({
teamA: 0,
teamB: 0
})
// ポイントを加算する
const addPoint = (team: 'teamA'|'teamB', point: number) => {
setScore({
...score,
[team]: score[team] + point
})
}
// 結果を表示する
const getResult = (): string => {
if (score.teamA === score.teamB) {
return `Draw`
} else {
return `${score.teamA > score.teamB ? 'teamA' : 'teamB'} Win!`
}
}
const value = {
score,
addPoint,
getResult
}
// valueにScoreBoardContextTypeの値を渡す
return <ScoreBoardContext.provider value={value}>{children}</ScoreBoardContext.provider>
}
// フックとして定義
const useScoreBoard = () => useContext(ScoreBoardContext)