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 디렉토리에 지정해둔 타입을 상속해서 새로 타입을 지정하면 될 것 같은데, 근데 넥스트랑 타입스크립트랑 리액트쿼리를 전부 처음 사용하다보니 개발 속도가 아무래도 좀 느려져서 나중에 시간 남으면 해 봐야겠다.