React

  • 投稿日:
  • 更新日:

【React】メディアクエリを使うカスタムフック

この記事では、Reactでメディアクエリを簡単に扱うためのカスタムフック useMediaQuery を作成する方法を紹介します。このカスタムフックを使用することで、コンポーネントのレンダリングをデバイスや画面幅に応じて最適化できます。

1. プロジェクトの構成

まず、プロジェクトの構成を確認しましょう。以下のファイルが必要です。

  • useMediaQuery.ts : カスタムフック useMediaQuery が定義されています。
  • minmaxDecision.ts : minmaxDecision 関数が定義されています。
  • vars.ts : 画面幅の定数 Device が定義されています。
  • types.ts : 型定義 DeviceType が定義されています。

2. useMediaQuery の解説

カスタムフック useMediaQuery のコードを見ていきましょう。

const useMediaQuery = (
  width: number | keyof DeviceType = Device.tb,
  minmax: 'min' | 'max' = 'max'
): boolean => {
  const [isMatch, setIsMatch] = useState(() => false)
  const breakpoint =
    typeof width === 'string' ? minmaxDecision(width, minmax) : width

  useEffect(() => {
    const isMatches = () =>
      window.matchMedia(`(${minmax}-width: ${breakpoint}px)`).matches
    const resetIsMatch = () => {
      const nowIsMatch = isMatches()
      setIsMatch(() => nowIsMatch)
    }


    resetIsMatch()
    window.addEventListener('resize', resetIsMatch)
    return () => {
      window.removeEventListener('resize', resetIsMatch)
    }
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [])

  return isMatch
}

export default useMediaQuery

useMediaQuery は2つの引数を受け取ります。

  • width : 画面幅の閾値(デフォルトは Device.tb
  • minmax : メディアクエリのタイプ('min' または 'max'。デフォルトは 'max')

useState を使って、 isMatch というステートを定義します。これは、メディアクエリがマッチしているかどうかを表す真偽値です。
breakpoint は、メディアクエリの閾値を計算します。width が文字列の場合、minmaxDecision 関数を使って計算されます。
useEffect を使って、resize イベントリスナーを設定します。このリスナーは、ウィンドウサイズが変更されたときに resetIsMatch 関数を呼び出し、isMatch の値を更新します。

3. minmaxDecision の解説

minmaxDecision 関数のコードを見ていきましょう。

const minmaxDecision = (
  width: number | keyof DeviceType,
  minmax: 'min' | 'max'
): number => {
  let _width: number = typeof width === 'number' ? width : Device[width]
  _width = minmax === 'max' ? _width - 1 : _width
  return _width
}

export default minmaxDecision

minmaxDecision 関数は2つの引数を受け取ります。

  • width: 画面幅の閾値またはデバイスタイプのキー
  • minmax: メディアクエリのタイプ('min' または 'max')

この関数は、width が数値の場合、そのままの値を使い、文字列の場合は Device オブジェクトから対応する数値を取得します。そして、minmax が 'max' の場合は、閾値から1を引いた値を返します。これにより、max-width クエリでは、閾値の1ピクセル手前までを対象とすることができます。

4. Device の解説

Device オブジェクトは、画面幅の定数を定義しています。以下のように定義されています。

export const Device: DeviceType = {
  pc: 1920,
  ct: 1136,
  tb: 768,
  sp: 428
}

これらの定数は、useMediaQueryminmaxDecision 関数で使用されます。プロジェクトでサポートするデバイスや画面サイズに応じて、適切な値を定義してください。

5. 使用方法

useMediaQuery を使って、コンポーネント内でメディアクエリを扱う方法を紹介します。

// 画面幅
export type DeviceType = {
  pc: 1920
  ct: 1136 // Contents
  tb: 768 //IPad Mini
  sp: 428 //iPhone14ProMax
}

import React from 'react'
import useMediaQuery from '@/hooks/useMediaQuery'
import { Device } from '@/styles/vars'

const MyComponent = () => {
  const isMobile = useMediaQuery(Device.sp, 'max')

  return (
    <div>
      {isMobile ? (
        <p>モバイル画面です</p>
      ) : (
        <p>モバイル画面以外です</p>
      )}
    </div>
  )
}

export default MyComponent

上記のコードでは、useMediaQuery を使って、画面幅が Device.sp よりも小さいかどうかを判定しています。isMobile の値に応じて、異なる内容を表示しています。
このように、useMediaQuery カスタムフックを使うことで、コンポーネント内で簡単にメディアクエリを扱うことができます。これにより、レスポンシブデザインを実現しやすくなります。

まとめ

この記事では、Reactでメディアクエリを簡単に扱うためのカスタムフック useMediaQuery の作成方法を紹介しました。また、minmaxDecision 関数や Device オブジェクトの定義方法についても説明しました。
useMediaQuery を使って、コンポーネントのレンダリングをデバイスや画面幅に応じて最適化することができます。これにより、レスポンシブデザインの実現が容易になります。
ぜひ、このカスタムフックをプロジェクトで活用して、レスポンシブなウェブアプリケーションを作成してください。