asoview! TECH BLOG

アソビュー株式会社のテックブログ

React Context APIを使ってメディアクエリを共通化してみた。

こんにちはフロントエンドエンジニアの指田です。
コロナの影響でフルリモートになって1か月ほど経ちました。
運動不足気味です。

さて、今回はレスポンシブ対応で利用するメディアクエリをReact Context APIを使って共有・取得がシンプルに実装できたので紹介します。

Context APIについて

簡単に言うとpropsで渡さなくても、コンポーネント間で共有する方法を提供しているAPIになります。
詳しくはReactの公式を確認いただければと思います! reactjs.org

メディアクエリについて

今回、ブレークポイントの設定・取得は「react-responsive」を使います。
Hookになっているので使いやすいです。 github.com

そして、ブレークポイントはこちらになります。

  • 560px未満をスマホと設定
  • 960px未満をタブレットと設定

hashimotosan.hatenablog.jp

さっそく実装を見てみましょう

// MediaQuery.tsx
import React, { useContext } from 'react'
import { useMediaQuery } from 'react-responsive'

const MediaQueryContext = React.createContext({
  isSmartPhone: false,
  isTablet: false,
  isMobile: false,
  isPc: false
})

export const MediaQueryProvider: React.FC = ({ children }) => {
  const isSmartPhone = useMediaQuery({ maxWidth: 559 })
  const isTablet = useMediaQuery({
    minWidth: 560,
    maxWidth: 959
  })
  const isMobile = isSmartPhone || isTablet
  const isPc = !isMobile

  return (
    <MediaQueryContext.Provider
      value={{ isSmartPhone, isTablet, isMobile, isPc }}
    >
      {children}
    </MediaQueryContext.Provider>
  )
}

export const useDeviceType = () => useContext(MediaQueryContext)

まずはContextProvideruseContextでメディアクエリを取得するための関数を作成します。
Contextexportせずに隠蔽します。
また、MediaQueryProviderはFunctionComponentで定義してuseMediaQueryにてブレークポイントを設定します。

これでほぼ実装はお終いです。

// App.tsx
import { MediaQueryProvider } from './MediaQuery'

render(
  <MediaQueryProvider>
    <App />
  </MediaQueryProvider>,
  document.querySelector('#app')
)

さきほどのMediaQueryProviderでアプリケーションを囲います。
これでどのコンポーネントでも利用できるようになります。

// HogePage.tsx
import { useDeviceType } from './MediaQuery'

const HogePage: React.FC = () => {
  const { isSmartPhone, isTablet, isMobile, isPc } = useDeviceType()

  return ()
}

export default HogePage

コンポーネントでuseDeviceTypeを利用してメディアクエリを取得して完了です!

まとめ

最初の実装はCustom Hookで作成していましたが、コンポーネント間でもっと簡単に共有したいと思いContext APIを利用しました。
他にも色々な用途(認証、翻訳等)でContext APIは利用できそうです。
※社内に展開したら認証処理はこの形に変更されました。