【React】useMediaを使ってレスポンシブ対応

## 概要
`use-media`ライブラリを使って、レスポンシブ対応してみます。
各デバイスのサイズに対応するBoolean値（isMobileSite, isTabletSite, isPcSite）を定義してContextを使いアプリケーション全体で共有します。共有されたBoolean値を参照し、各デバイス毎に用意したコンポーネントを出し分けすることでレスポンシブ対応を行います。

## 環境
- react v17.0.2
- use-media v1.4.0
- typescript v4.1.2

## インストール
```shell
# npmでのインストール
$ npm install --save use-media
# yarnでのインストール
$ yarn add use-media
```

## 実装
### MediaQueryProvider
下記役割のコンポーネント、カスタムフックを定義してexportします。

- デバイスのサイズに対応するBoolean値をContextでアプリケーション全体に共有するための`MediaQueryProvider`
- Providerコンポーネントから共有されたBoolean値を取得するカスタムフック`useMediaQueryContext`

各デバイスのブレイクポイントは[https://hashimotosan.hatenablog.jp/entry/2020/12/06/182327](https://hashimotosan.hatenablog.jp/entry/2020/12/06/182327)を参考にしました。

```typescript:Components/Provider/MediaQueryProvider.tsx
import React, { createContext, FC, useContext, useMemo } from 'react';
import useMedia from 'use-media';

type Props = {
  children: React.ReactNode;
};

// アプリケーション全体で共有する値の定義
type Context = {
  // モバイルか？
  isMobileSite: boolean;
  // タブレットか？
  isTabletSite: boolean;
  // PCか？
  isPcSite: boolean;
};

// Contextの生成
// デフォルトはPCとする。
const MediaQueryContext = createContext<Context>({
  isMobileSite: false,
  isTabletSite: false,
  isPcSite: true,
});

// 各デバイスでのサイズを定義
const mediaQueries = {
  mobile: '(max-width: 519px)',
  tablet: '(min-width: 520px) and (max-width: 959px)',
  pc: '(min-width: 960px)',
};

export const MediaQueryProvider: FC<Props> = ({ children }: Props) => {
  const isMobileSite = useMedia(mediaQueries.mobile);
  const isTabletSite = useMedia(mediaQueries.tablet);
  const isPcSite = useMedia(mediaQueries.pc);
  const value = useMemo(() => ({ isMobileSite, isTabletSite, isPcSite }), [
    isMobileSite,
    isTabletSite,
    isPcSite,
  ]);

  return (
    <MediaQueryContext.Provider value={value}>
      {children}
    </MediaQueryContext.Provider>
  );
};

export const useMediaQueryContext = (): Context =>
  useContext(MediaQueryContext);
```

### App
MediaQueryProviderがメインのコンポーネントを囲うようにします

```App.tsx
import React from 'react';
import MainComponent from 'components/pages/MainComponent';
import { MediaQueryProvider } from 'components/provider/MediaQueryProvider';

export default function App() {
  return (
    <MediaQueryProvider>
      <MainComponent />
    </MediaQueryProvider>
  );
}
```

### MainComponent
`useMediaQueryContext`を使用し、全体に共有されたBoolean値を取得し、Trueの場合に対応するコンポーネントを出力するようにします。

```typescript:components/pages/MainComponent
import React, { FC } from 'react';
import MobileContents from 'components/templates/MobileContents';
import TabletContents from 'components/templates/TabletContents';
import PcContents from 'components/templates/PcContents';
import { useMediaQueryContext } from 'components/provider/MediaQueryProvider';

const MainComponent: FC = () => {
  const { isMobileSite, isTabletSite, isPcSite } = useMediaQueryContext();

  return (
    {isMobileSite && (
      <MobileContents />
    )}
    {isTabletSite && (
      <TabletContents />
    )}
    {isPcSite && (
      <PcContents />
    )}
  );
};

export default MainComponent;
```

## まとめ
今回はReactでレスポンシブ対応を実装する`useMedia`を使ってみました。
各デバイスに対応するコンポーネントを作成することで、簡単にレスポンシブ対応が可能になります。

今後も試してみたライブラリなどを共有していきます。

## 参考サイト
- [https://github.com/streamich/use-media](https://github.com/streamich/use-media)
- [https://rpf-noblog.com/2021-01-17/react-hook-usemedia/](https://rpf-noblog.com/2021-01-17/react-hook-usemedia/)
- [https://hashimotosan.hatenablog.jp/entry/2020/12/06/182327](https://hashimotosan.hatenablog.jp/entry/2020/12/06/182327)