useMediaQuery로 반응형 웹 구현하기

8

custom-hooks

목록 보기
1/2
post-thumbnail

반응형

오늘날 PC가 아닌 모바일, 태블릿으로 웹을 드나들 수 있게되면서, 반응형 웹에 대한 수요가 높아졌다.
CSS에서도 MediaQuery를 이용하여 반응형웹을 구현할 수 있지만,
React에서 컴포넌트 내용을 다르게 보여준다던지, 구조가 달라진다던지 하는 부분에 있어서 CSS로만 처리를 하는 데에는 어려움이 많다.

useMediaQuery

이와같은 생각을 다들 하는건지, 이미 여러 라이브러리에 관련된 훅이 많이 나와있었다.
하지만 훅 하나만 필요하기도 하고, 어떻게 구현된건지 학습할겸 useHooks-ts 라이브러리의 공식문서를 보고 그대로 옮기기로 했다.

import { useEffect, useState } from 'react'

export function useMediaQuery(query: string): boolean {
  const getMatches = (query: string): boolean => {
    // Prevents SSR issues
    if (typeof window !== 'undefined') {
      return window.matchMedia(query).matches
    }
    return false
  }

  const [matches, setMatches] = useState<boolean>(getMatches(query))

  function handleChange() {
    setMatches(getMatches(query))
  }

  useEffect(() => {
    const matchMedia = window.matchMedia(query)

    // Triggered at the first client-side load and if query changes
    handleChange()

    // Listen matchMedia
    if (matchMedia.addListener) {
      matchMedia.addListener(handleChange)
    } else {
      matchMedia.addEventListener('change', handleChange)
    }

    return () => {
      if (matchMedia.removeListener) {
        matchMedia.removeListener(handleChange)
      } else {
        matchMedia.removeEventListener('change', handleChange)
      }
    }
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [query])

  return matches
}

훅 구현사항을 요약하자면,
matchMedia (query와 화면이 일치하는지 여부)에 이벤트를 달아,
화면이 바뀔때마다 감지해 matches값을 반환하는 훅이다.

사용하기

 const isDesktop = useMediaQuery("(min-width: 1024px)");

컴포넌트 상위에 useMediaQuery와 함께 query내용을 넣어주면 true or false값을 리턴한다.
width가 1024보다 크면 isDesktop이 true인 셈이다.

응용하기

모든 컴포넌트에 isDesktop을 사용하기 위해서 일일히 쿼리를 작성해야하는 것은 매우 피곤한 일이다.
또한, 화면 사양이 추가됨에 따라 컴포넌트마다 정의되는 쿼리는 늘어날 것이다.
재사용성과 추상화를 위해서 desktop, mobile, tablet 값을 리턴하는 hook을 만들어보자.

import { useMediaQuery } from "./useMediaQuery";

export default function useDeviceSize() {
  const isDesktop = useMediaQuery("(min-width: 1024px)");
  const isTablet = useMediaQuery("(min-width:768px) and (max-width:1023px)");
  const isMobile = useMediaQuery("(max-width:767px)");

  return { isDesktop, isTablet, isMobile };
}

이와같이 custom hook을 정의하고,

사용하고 싶은 컴포넌트에서 단순히 useDeviceSize를 불러와주면된다.

import useDeviceSize from "./hooks/useDeviceSize";

function 컴포넌트() {
  const { isDesktop, isMobile, isTablet } = useDeviceSize();
  if (isDesktop) {
    return <div>Desktop</div>;
  }
  if (isTablet) {
    return <div>isTablet</div>;
  }
  if (isMobile) {
    return <div>isMobile</div>;
  }
  
  return <div>Other</div>;
}

1개의 댓글

comment-user-thumbnail
2023년 8월 29일

좋은 라이브러리네요. 잘 봤습니다!

답글 달기

관련 채용 정보

Powered by GraphCDN, the GraphQL CDN