javascriptのwindow.matchMediaでreactのカスタムフック(useMediaQuery)を作成してレスポンシブ対応

2021年1月18日React

Webページをレスポンシブ対応する場合に、cssの@media クエリを利用する以外にjavascriptのwindows.matchMedia()を利用する方法があります。

javascriptのwindow.matchMedia()の使用例

MDNのサンプルそのままですが、以下のようにcssのメディアクエリで指定する文字列を引数に指定することで処理を分岐させることができます。

if (window.matchMedia( "(min-width: 400px)" ).matches) {
  /* ビューポートの幅が 400 ピクセル以上の場合のコードをここに */
} else {
  /* ビューポートの幅は 400 ピクセル未満の場合のコードをここに */
}

Reactカスタムフックを作成(useMediaQuery)

javascriptのwindow.matchMedia()を利用して、Reactのカスタムフックを作成してみました。

ここでは、単純にPCサイズなのかモバイル(スマホ)サイズなのかを判定できるようにしました。

  • isPc : 横幅が576px以上の場合 => True
  • isMoblie : 横幅が575px以下の場合 => True
import { useState, useEffect } from 'react';

export const useMediaQuery = () => {

  const [mq, setMq] = useState({
    isPc: window.matchMedia('screen and (min-width: 576px)').matches,
    isMoblie: window.matchMedia('screen and (max-width: 575px)').matches
  });

  useEffect(() => {
    const onResize = () => {
      setMq({
        isPc: window.matchMedia('screen and (min-width: 576px)').matches,
        isMoblie: window.matchMedia('screen and (max-width: 575px)').matches
      });
    }

    window.addEventListener('resize', onResize);
    window.addEventListener('load', onResize);

    return () => {
      window.removeEventListener('resize', onResize);
      window.removeEventListener('load', onResize);
    }
  });

  return mq
}
point

resize()だけの場合、画面表示時に動作しないため、resize()とload()の両方のイベントで実行されるようにします。

useMediaQueryの利用例

作成したカスタムフック(useMediaQuery)を利用してメニューの表示切替を行う場合のサンプルです。

  • PCサイズの場合:メニューをヘッダーに表示
  • モバイルサイズの場合:メニューをフッターに表示
import React from 'react'
import { TopNavbar } from '../components/menu/TopNavbar'
import { BottomNavbar } from '../components/menu/BottomNavbar'
import { useMediaQuery } from '../components/use/useMediaQuery'

import { ThemeProvider } from "styled-components";
import { theme } from "./Theme";


export const GenericTemplate: React.FC = ({ children }) => {
  const mq = useMediaQuery();

  return (
    <ThemeProvider theme={theme}>
      <header>
      {mq.isPc && <TopNavbar />}
      </header>
      <main>
        {children}
      </main>
      {mq.isMoblie && <BottomNavbar />}
    </ThemeProvider>
  );
}

React

Posted by snow