[React-Query] 함수 컨텍스트 활용하기

임홍원·2024년 1월 30일
post-thumbnail

프로젝트를 진행하던도중 Query Function 에 매개변수가 많아져서 리팩토링이 필요해졌다.
따라서 매개변수를 활용하는 방법이 무엇이 있을까 찾아보던 와중 함수 컨텍스트가 존재했다.

QueryFunctionContext

QueryFunctionContextqueryFn에 인수로 전달되는 객체이다.

const BookmarkCamp = () => {
  const params = useParams();
  const userId = params.id as string;
  const { data, isLoading, isError } = useQuery<LikeCampType>({
    queryKey: ['mypage', 'bookmark', 'camp', userId],
    queryFn: getUserLikeCamp,
    refetchOnWindowFocus: true,
  });
import { LikeCampType } from '@/types/profile';
import { QueryFunctionContext } from '@tanstack/react-query';

export const getUserLikeCamp = async ({ queryKey }: QueryFunctionContext): Promise<LikeCampType> => {
  const [_, __, ___, userId] = queryKey;
  const res = await fetch(`/api/profile/${userId}/camp/like`, {
    method: 'GET',
  });

  const fetchData = await res.json();
  return fetchData;
};

객체 구조분해 할당으로 queryKey를 받고 QueryFunctionContext type으로 선언한다.

Query Key Factories

키를 빌드하기 위한 타입 안전한 쿼리 키 팩토리가 있는 경우,
해당 팩토리의 리턴 타입을 사용하여 QueryFunctionContext를 입력할 수 있다.

const todoKeys = {
  all: ['todos'] as const,
  lists: () => [...todoKeys.all, 'list'] as const,
  list: (state: State, sorting: Sorting) =>
    [...todoKeys.lists(), state, sorting] as const,
}

const fetchTodos = async ({
  queryKey,
}: 
// factory 함수의 리턴값인 키만 허용
QueryFunctionContext<ReturnType<typeof todoKeys['list']>>) => {
  const [, , state, sorting] = queryKey
  const response = await axios.get(`todos/${state}?sorting=${sorting}`)
  return response.data
}

export const useTodos = () => {
  const { state, sorting } = useTodoParams()

  // ✅ 쿼리키 팩토리로 키를 빌드
  return useQuery(todoKeys.list(state, sorting), fetchTodos)
}

위의 예에서는 쿼리키를 키 팩토리의 list 함수가 반환하는 것과 동일하게 설정한다.
const assertions을 사용하기 때문에 모든 키는 엄격하게 타입이 지정된 튜플이 될 것이다.
따라서 해당 구조를 따르지 않는 키를 사용하려고 하면 타입 오류가 발생한다.

Object Query Key

배열의 구조분해는 named property를 가져올 때 위험할 수 있다. undefined인지 체크해주지 않는다.

const [, , state, sorting] = queryKey

명명된 분해를 사용할 수 있기 때문에 객체는 이 문제를 정말 잘 해결한다.
또한 쿼리 무효화를 위한 퍼지 일치(추상적인 키를 입력할 수록 매칭 범위가 커지는 일치)가
배열의 경우와 객체에 대해 동일하게 작동하기 때문에 쿼리 키로서 단점이 없다.

const todoKeys = {
  //  all keys are arrays with exactly one object
  all: [{ scope: 'todos' }] as const,
  lists: () => [{ ...todoKeys.all[0], entity: 'list' }] as const,
  list: (state: State, sorting: Sorting) =>
    [{ ...todoKeys.lists()[0], state, sorting }] as const,
}

const fetchTodos = async ({
  //  extract named properties from the queryKey
  queryKey: [{ state, sorting }],
}: QueryFunctionContext<ReturnType<typeof todoKeys['list']>>) => {
  const response = await axios.get(`todos/${state}?sorting=${sorting}`)
  return response.data
}

export const useTodos = () => {
  const { state, sorting } = useTodoParams()

  return useQuery(todoKeys.list(state, sorting), fetchTodos)
}
//  remove everything related to the todos feature
queryClient.removeQueries([{ scope: 'todos' }])

//  reset all todo lists
queryClient.resetQueries([{ scope: 'todos', entity: 'list' }])

//  invalidate all lists across all scopes
queryClient.invalidateQueries([{ entity: 'list' }])
profile
Frontend Developer

0개의 댓글