sumini dev

posts

Query Parameter Hook 만들기

profile-image

Choi su min

window.location.search를 읽어, 사용하기 좋은 object 형태로 만들어야 한다.

이를 구현할 수 있는 여러 라이브러리(react-router, react-router-dom, use-react-router, …)가 있지만, 모두 Provider를 필요로 한다.

가볍게 아무데서나 호출할 수 있는 custom hook을 만들어보자!

구현

import { useEffect, useState } from 'react';

export const useQueryParams = (): { [name: string]: string } => {
  const [params, setParams] = useState({});

  useEffect(() => {
    const temp = {};
    new URLSearchParams(window.location.search).forEach((value, key) => {
      temp[key] = value;
    });
    setParams(temp);
  }, []);

  return params;
};
hooks/route.ts

URLSearchParams 객체를 사용해 간단하게 구현했다.

적용

...
import { useQueryParams } from 'hooks/route';

export default function PostsPage({ data, location }: PageProps<any>) {
  const { tag } = useQueryParams();

  return (
    <Wrapper>
      <PageTitle>{tag ? `Tag - ${tag}` : 'Posts'}</PageTitle>
      ...
    </Wrapper>
  );
}
pages/posts.tsx

성공적으로 작동한다!

이슈1 : 초기렌더링

Initial rendering

ssr 또는 static generate 환경에서 페이지에 처음 접근하거나 새로고침했을 때, 기본 값인 Posts가 노출되는 것을 확인할 수 있다!

이는 갯츠비의 static rendering 시점에 useEffect가 실행되지 않았고, 그로 인해 tag 변수에 undefined 값이 들어갔기 때문이다.

따라서 아래와 같이 사용할 경우 에러가 난다 😥

...
import { useQueryParams } from 'hooks/route';

export default function Hello() {
  const { tag } = useQueryParams();

  return (
    <Text>태그의 길이는 {tag.length}</Text>
  ); // TypeError: Cannot read property 'length' of undefined

  return (
    <Text>태그의 길이는 {tag?.length}</Text>
  ); // OK
}

이처럼 client-side에서만 존재하는 값을 다룰 때는 optional chaining이나 별도의 state를 사용하여 undefined를 대응할 필요가 있다.

이슈2 : 구형 브라우저 대응

can i use

can i use에 검색해보면 IE를 포함한 대부분의 구버전 브라우저에서 URLSearchParams를 지원하지 않는다! 전세계 94.3%가 정상적으로 사용가능하지만, 서비스 특성상 IE를 지원해야한다면 어쩔 수 없이 polyfill을 준비해야만 한다.


import { useEffect, useState } from 'react';

export const useQueryParams = (): { [name: string]: string } => {
  const [params, setParams] = useState({});

  useEffect(() => {
    const temp = {};
    window.location.search.replace(/[?&]+([^=&]+)=([^&]*)/gi, ((
      _str,
      key,
      value
    ) => {
      temp[key] = value;
    }) as () => string);
    setParams(temp);
  }, []);

  return params;
};
hooks/route-polyfill.ts

String.prototype.replace()를 이용해 정상적으로 구현했다!



REACT8개 더보기

HOOKS1개 더보기

모든 글 보기

이전 글

Scroll Direction Hook 만들기

다음 글

Django migration 롤백, 재생성 방법

profile-image

Choi su min

Sogang Univ.

AWESOME.DEV Lead

Anyang, Gyeong-gi, Republic of Korea

github-iconmail-iconrss-icon