본문 바로가기

카테고리 없음

typescript generic 공부2: useInfiniteQuery 초기 pageParams 지정

typescript generic 공부: react query 공통 type 모듈 지정 (https://honey-dev.tistory.com/5) 이 글에서 연결됨

 

 

react bulletproof 규칙을 따르다보니 react query를 사용할 때 axios 요청함수 (query function)과 react query hooks를 분리해서 코드를 짜는 중이다. 무한스크롤을 useInfiniteQuery를 이용해서 구현하고 있으며, 팀원이 짠 코드를 베이스로 하여 동일 컴포넌트를 재사용할 수 있게 개발하는 중이다.

 

pageParams 초기값을 지정해주고 싶었는데 어떻게 주면 좋을지 구조를 보면서 고민하다가, initial data를 세팅하는 건 아닌 것 같아서 아예 params로 넘겨주는 값에서 page를 제외시켰다. 즉 page 키에 해당하는 pageParam은 useInfiniteQuery에서 최초 생성하고 또 getNextPageParam으로 받아오면서 컴포넌트에서 넘겨주는 params 객체에 page: pageParam 형태로 추가하게 되어서, pageParam의 컨트롤을 모두 useInfiniteQuery에서 하게 된다.

 

이 과정에서 타입에러 해결을 위해 컴포넌트에서 넘겨주는 params 객체의 타입과 api 요청을 할 axios params 객체의 타입을 상속해서 두 개 만들어 줬다. 

 

1. 기존 코드

  const { status, data, fetchNextPage } = useCodeList({
    params: {
      page: pageParam || 0,
      sort: sort,
      size: size,
      keyword: usingKeyword,
      tagIdList: stringTagIdList,
    },
    config: {
      getNextPageParam: (lastPage) =>
        lastPage.nextPage === -1 ? undefined : lastPage.nextPage,
    },
  });
type CodeListDTO = {
  sort: SortOption;
  page: number;
  size: number;
  keyword: string;
  tagIdList: string;
};

export const getCodeList = (params: CodeListDTO): Promise<CodeListResult> => {
  return axios.get("/code", { params });
};

type QueryFnType = typeof getCodeList;

type UseCodeListOptions = {
  params: CodeListDTO;
  config?: InfiniteQueryConfig<QueryFnType>;
};

export const useCodeList = ({ params, config }: UseCodeListOptions) => {
  return useInfiniteQuery<ExtractFnReturnType<QueryFnType>>({
    ...config,
    queryKey: ["codeList", params],
    queryFn: () => getCodeList(params),
  });
};

 

2. 변경 코드

  const { status, data, fetchNextPage } = useCodeList({
    params: {
      sort: sort,
      size: size,
      keyword: usingKeyword,
      tagIdList: stringTagIdList,
    },
    config: {
      getNextPageParam: (lastPage) =>
        lastPage.nextPage === -1 ? undefined : lastPage.nextPage,
    },
  });
type CodeListParams = {
  sort: SortOption;
  size: number;
  keyword: string;
  tagIdList: string;
};

type CodeListDTO = {
  page: number;
} & CodeListParams;

export const getCodeList = (params: CodeListDTO): Promise<CodeListResult> => {
  return axios.get("/code", { params });
};

type QueryFnType = typeof getCodeList;

type UseCodeListOptions = {
  params: CodeListParams;
  config?: InfiniteQueryConfig<QueryFnType>;
};

export const useCodeList = ({ params, config }: UseCodeListOptions) => {
  return useInfiniteQuery<ExtractFnReturnType<QueryFnType>>({
    ...config,
    queryKey: ["codeList", params],
    queryFn: ({ pageParam = 0 }) => getCodeList({ ...params, page: pageParam }),
  });
};

 

3. 추가사항

구조 정리하면서 보다 보니 컴포넌트에서 config를 넘겨주는 방식도 조금 개선의 여지가 있어 보여서, 이 부분도 정리하고 싶다. react query의 configuration은 api 디렉토리 안에서 세팅하는 방식이 좋을 것 같아서, 아마 기본 공통 타입으로 lib 디렉토리에 지정해둔 타입을 상속해서 새로 타입을 지정하면 될 것 같은데, 근데 넥스트랑 타입스크립트랑 리액트쿼리를 전부 처음 사용하다보니 개발 속도가 아무래도 좀 느려져서 나중에 시간 남으면 해 봐야겠다.