useContextを学びました

komosyu
React

目次

  1. はじめに
  2. useContext とは?
  3. 具体的に役立つ場面
  4. 具体的な使い方
    1. 1.値を定義するコンポーネント内でcreateContextreactから読み込みます
    2. 2.context を定義します
    3. 3.context を使用したいコンポーネントで読み込みます
  5. state の受け渡し
    1. 1.状態管理をおこなう親コンポーネントを Provider で囲む
    2. 2.状態を更新する
    3. 3.状態を取得する
  6. context を分離する
    1. 1.状態の管理のみをおこなうStateContext.jsxを作成する
    2. 2.Parent.jsxで使用してみる
    3. 3.更新・取得もまとめる
  7. 参考
  8. さいごに

はじめに

今回もまた React の hooks について書いてきたいと思います。
useReducer の時と同様に、Udemy で勉強させていただいている好きな講座のアウトプットとしての記事となりそうですが…。

今回は useContext について取り上げたいと思います!

useContext とは?

まず、useContext とは React の hooks のひとつで、props や state をグローバルに共有することができるものです。

親コンポーネントで定義した値を子孫コンポーネントで使いたい場合、親コンポーネント=>子コンポーネント=>子孫コンポーネント?といった感じで props のバケツリレーをすると非常に冗長になってしまうところを、useContext を使うことでシンプルにまとめることができるようになります!

具体的に役立つ場面

useContext を使わない場合だと、こんな感じでバケツリレーすることになります。

親コンポーネントのParent.jsx

import Child from '../lower/Child'

const Parent = () => {
  const props = 'propsValue'
  return <Child props={props} />
}
export default Parent

子コンポーネントのChild.jsx

import GrandChild from './GrandChild'

const Child = ({ props }) => {
  return <GrandChild props={props} />
}
export default Child

孫コンポーネントのGrandChild.jsx

const GrandChild = ({ props }) => {
  // 'propsValue'が表示される
  return <p>{props}</p>
}
export default GrandChild

props を少し深い階層で渡したいだけなのに、毎回こんな処理を行うのは面倒ですよね…。
コードがあまり気持ちよくないですね。

なので、最初に props を定義しているParent.jsxuseContext を使ってバケツリレーをせずにGrandChild.jsxで props を呼び出してみましょう。

具体的な使い方

それでは実際にuseContextを使った場合の具体的な使い方を見ていきましょう!

1.値を定義するコンポーネント内でcreateContextreactから読み込みます

import { createContext } from 'react'

import Child from '../lower/Child'
const Parent = () => {
  return <Child props={props} />
}
export default Parent

2.context を定義します

createContextの引数に、これまで props で渡していた値を渡します。

import { createContext } from 'react'
import Child from '../lower/Child'

// 他のコンポーネントで呼び出すのでexportしておきましょう
export const ContextValue = createContext(
  // 他のコンポーネントに渡したい値を入れる
  'propsValue'
)

const Parent = () => {
  return <Child props={props} />
}
export default Parent

3.context を使用したいコンポーネントで読み込みます

孫コンポーネントのGrandChild.jsxで値を呼び出してみましょう。
内容としては、useContext の引数にParent.jsxで定義したContextValueを渡すことでその値を取得することができるようになります。

import { useContext } from 'react'
import { ContextValue } from '../../Parent'

const GrandChild = () => {
  const propsValue = useContext(ContextValue)
  return (
    // 'propsValue'が表示される
    <p>{propsValue}</p>
  )
}
export default GrandChild

このように useContext を使って処理をすることで、props のバケツリレーをする必要がなくなり、Child.jsxにムダな処理を書く必要がなくなって面倒くささが解消された気がしますね!

state の受け渡し

上記では props の受け渡しの例を見てみましたが、実際の案件のなかでは state もグローバルで管理したい場面も当然出てくると思うので、その実装方法も解説していきます。

今回の場合はGrandChild.jsxで値を更新して、Child.jsxで値を取得してみます!

1.状態管理をおこなう親コンポーネントを Provider で囲む

state を管理する時には先ほどとは大きく違う点があって、createContextで定義した値のProviderでそれぞれのコンポーネントを囲む必要があります。

そして、その中のvalueで設定された値が useContext を通して取得できるようになります。

今回は state を取得したいので、valueにはuseStateで定義しておいた値を渡しておきましょう。

import { createContext, useState } from 'react'
import Child from '../lower/Child'
import GrandChild from '../lower/GrandChild'

export const ContextValue = createContext()

const Parent = () => {
  const [state, setState] = useState()
  return (
    <ContextValue.Provider value={[state, setState]}>
      <Child />
      <GrandChild />
    </ContextValue.Provider>
  )
}
export default Parent

2.状態を更新する

先ほどのParent.jsxContextValue.Providervalueに渡した値を呼び出して使います。

今回のGrandChild.jsxでは状態の更新のみをおこなうので、valueに渡された配列の 1 番目の値であるsetStateを使えるようにしておきます。

import { useContext } from 'react'
import { ContextValue } from '../../Parent'

const GrandChild = () => {
  // 更新用の値のみ呼び出す
  const [, setState] = useContext(ContextValue)
  // 値の更新
  const handleCoutUp = () => {
    setState((prev) => prev + 1)
  }
  return <button onClick={handleCoutUp}>count up</button>
}
export default GrandChild

3.状態を取得する

更新時と同様にuseContextを使って state を呼び出していますが、今回のChild.jsxでは状態の取得のみをおこなうので、valueに渡された配列の 0 番目の値であるstateを使えるようにしておきます。

import { useContext } from 'react'
import { ContextValue } from '../../Parent'

const Child = () => {
  // 表示用の値のみ呼び出す
  const [state] = useContext(ContextValue)
  // 値の表示
  return <p>現在のカウント: {state}</p>
}
export default Child

context を分離する

上記でも問題ない挙動にはなりますが、状態の管理を別で持たせておくことでコンポーネントごとの役割を明確にして再利用性も高めることができると思うので、ここでコードの整理をしてみましょう。

1.状態の管理のみをおこなうStateContext.jsxを作成する

先ほどContextValueを定義していたParent.jsxから切り離して、この中で処理をまとめておきます。

import { useState } from 'react'

export const ContextValue = createContext()

export const StateContext = ({ children }) => {
  const [state, setState] = useState()
  return (
    <ContextValue.Provider value={[state, setState]}>
      {children}
    </ContextValue.Provider>
  )
}

2.Parent.jsxで使用してみる

StateContext.jsxを使うまでは、createContextuseStateを定義したりでごちゃごちゃしていましたが、それらの処理はStateContextにまとめておいたので非常にシンプルに整理することができました。

import { StateContext } from '../context/StateContext'
import Child from '../lower/Child'
import GrandChild from '../lower/GrandChild'

const Parent = () => {
  return (
    <StateContext>
      <Child />
      <GrandChild />
    </StateContext>
  )
}
export default Parent

3.更新・取得もまとめる

今のままでは更新・取得をおこなう際にuseContextContextValueの両方を呼び出して使う必要があるので、こちらもStateContext.jsxでまとめておきましょう!

import { useState, useContext } from 'react'

export const ContextValue = createContext()

export const StateContext = ({ children }) => {
  const [state, setState] = useState()
  return (
    <ContextValue.Provider value={[state, setState]}>
      {children}
    </ContextValue.Provider>
  )
}
// ここでuseContextを使ってProviderのvalueを呼び出せるようにしておく
export const useValue = () => useContext(StateContext)

更新する方はこう

import { useValue } from '../context/StateContext'

const GrandChild = () => {
  // 更新用の値のみ呼び出す(useContext(ContextValue)からuseValue()に書き換える)
  const [, setState] = useValue()
  // 値の更新
  const handleCoutUp = () => {
    setState((prev) => prev + 1)
  }
  return <button onClick={handleCoutUp}>count up</button>
}
export default GrandChild

取得する方はこう

import { useValue } from '../context/StateContext'

const Child = () => {
  // 表示用の値のみ呼び出す(useContext(ContextValue)からuseValue()に書き換える)
  const [state] = useValue()
  // 値の表示
  return <p>現在のカウント: {state}</p>
}
export default Child

先ほどと比較して、だいぶすっきりになったコードになってみやすくなりましたね。

参考

さいごに

これまでグローバルな状態管理をしたことがなくはなかったのですが、ライブラリの言う通りにでなんとなく実装していたところがあったので、こうして React の hooks として用意されている useContext の使い方をちゃんと学ぶことが理解が深まった気がするのでよかったです。

やはり基礎の学び直しは大切ですね。