React Query(= TanStack Query)는
서버 상태 관리
를 위한 최고 라이브러리 중 하나이다.
- 별도의 설치나 구성이 필요 없이 바로 사용 가능하고,
- 설정이 매우 편하고 간단하며,
- 커스터마이징 될 수 있다.
기술적 측면에서 React Query의 장점
- React query 로직을 사용해서 복잡하고 이해하기 어려운 코드를 제거할 수 있다.
- 새로운 서버 상태 데이터 소스 연결에 대한 걱정 없이 애플리케이션을 더 유지 가능하게 하고 새로운 기능들도 쉽게 구축할 수 있게 한다.
- 사용자에게 있어서 더 빠르고 반응성이 더 뛰어나다.
- 잠재적으로 대역폭을 절약하고 메모리 성능을 증가시킨다.
import { QueryClient } from "@tanstack/react-query";
const queryClient = new QueryClient({
defaultOptions: {
queries: {
staleTime: Infinity,
// ...
},
},
});
캐시
와 상호작용할 수 있다.import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
const queryClient = new QueryClient({/* options */});
function App() {
return (
<QueryClientProvider client={queryClient}>
...
</QueryClientProvider>
)
}
QueryClientProvider
를 최상단에서 감싸주고, QueryClient 인스턴스를 client
props로 넣어서 애플리케이션에 연결해야 한다.하나의 객체
만 받는다.queryKey
, queryFn
인자는 필수 값이다.// 실제 예제
const getAllSuperHero = async () => {
return await axios.get("http://localhost:4000/superheroes");
};
const { data, isLoading } = useQuery({
queryKey: ["super-heroes"],
queryFn: getAllSuperHero,
});
queryKey
const getSuperHero = async ({ queryKey }: any) => {
const heroId = queryKey[1]; // queryKey: ['super-hero', '3'] 도 가능
return await axios.get(`http://localhost:4000/superheroes/${heroId}`);
};
const useSuperHeroData = (heroId: string) => {
return useQuery({
queryKey: ["super-hero", heroId],
queryFn: getSuperHero,
});
};
배열
로 지정해줘야 한다.useQuery는
queryKey
를 기반으로 쿼리 캐싱을 관리한다.
- 만약 쿼리가 특정 변수에 의존한다면 배열에 이어서 줘야 한다. ex: ["super-hero", heroId, ...]
- 특정 쿼리의 접근이 필요할 때 초기에 설정해둔 포맷을 지켜줘야 그대로 쿼리에 접근 가능하다
queryFn
queryFn
은 Promise
를 반환하는 함수를 넣어야한다.const getSuperHero = async (heroId: string) => {
return await axios.get(`http://localhost:4000/superheroes/${heroId}`);
};
const useSuperHeroData = (heroId: string) => {
return useQuery({
queryKey: ["super-hero", heroId],
queryFn: () => getSuperHero(heroId),
});
};
options
const {
data,
dataUpdatedAt,
error,
errorUpdatedAt,
failureCount,
failureReason,
fetchStatus,
isError,
isFetched,
isFetchedAfterMount,
isFetching,
isInitialLoading,
isLoading,
isLoadingError,
isPaused,
isPending,
isPlaceholderData,
isRefetchError,
isRefetching,
isStale,
isSuccess,
refetch,
status,
} = useQuery(
{
queryKey,
queryFn,
gcTime,
enabled,
networkMode,
initialData,
initialDataUpdatedAt,
meta,
notifyOnChangeProps,
placeholderData,
queryKeyHashFn,
refetchInterval,
refetchIntervalInBackground,
refetchOnMount,
refetchOnReconnect,
refetchOnWindowFocus,
retry,
retryOnMount,
retryDelay,
select,
staleTime,
structuralSharing,
throwOnError,
},
queryClient,
)
pending
: 쿼리 데이터가 없고, 쿼리 시도가 아직 완료되지 않은 상태{ enabled: false }
상태로 쿼리가 호출되면 이 상태로 시작된다.이를 위해 enabled
옵션을 사용한다.
// Get the user
const { data: user } = useQuery({
queryKey: ['user', email],
queryFn: getUserByEmail,
})
const userId = user?.id
// Then get the user's projects
const {
status,
fetchStatus,
data: projects,
} = useQuery({
queryKey: ['projects', userId],
queryFn: getProjectsByUser,
// userId가 존재할 때까지 쿼리를 실행하지 않음
**enabled: !!userId,**
})
error
: 에러 발생했을 때 상태success
: 쿼리 함수가 오류 없이 요청 성공하고 데이터를 표시할 준비가 된 상태fetching
: 쿼리가 현재 실행 중인 상태paused
: 쿼리를 요청했지만, 잠시 중단된 상태 (network mode 와 연관)idle
: 쿼리가 현재 아무 작업도 수행하지 않는 상태isFetching && isPending
과 동일true
true
🤔 status와 fetchStatus를 나눠서 다루는 이유?
- status : data가 있는지 없는지에 대한 상태를 의미한다.
- fetchStatus : 쿼리 즉, queryFn 요청이 진행 중인지 아닌지에 대한 상태를 의미한다.
staleTime (number | Infinity)
0분
이다.gcTime <number | Infinity>
inactive
상태일 때 캐싱된 상태로 남아있는 시간(밀리초)을 의미한다.가비지 콜렉터
로 수집된다.inactive
된 시점을 기준으로 캐시 데이터를 결정한다.5분
이며, SSR환경에서는 Infinity
다.refetchOnMount (boolean | “always”)
stale
상태일 경우 마운트 시마다 refetch를 실행하는 옵션이다.true
이다.always
로 설정하면 마운트 시마다 매번 refetch 를 실행한다.false
로 설정하면 최초 fetch 이후에는 refetch 하지 않는다.refetchOnWindowFocus (boolean | “always”)
stale
상태일 경우 윈도우 포커싱
될 때마다 refetch를 실행하는 옵션이다.true
이다.retry (boolean | number | (failureCount: number, error: TError) => boolean)
특정 횟수
만큼 재시도 하는 옵션3번
재시도, 서버 환경에서는 0번
재시도한다.true
로 설정하면 쿼리 실패시 무한 재시도한다.false
로 설정하면 재시도 하지 않는다.
v4
까지 있던 onSuccess, onError, onSettled Callback은useQuery
옵션에서는 더 이상 사용하지 않는다. 단,useMutation
에서는 사용 가능하다.
onSuccess ((data: TDdata) ⇒ void)
onError ((error: TError) ⇒ void)
onSettled ((data?: TData, error?: TError) ⇒ void)
select (data: TData) => unknown
placeholderData (TData | (previousValue: TData | undefined; previousQuery: Query | undefined,) => TData)
pending
상태인 특정 쿼리에 대한 placeholder data로 사용된다.notifyOnChangeProps (string[] | "all" | (() => string[] | "all"))
- 쿼리의 특정 프로퍼티들이 변경되었을 때만 리랜더링이 발생하도록 설정 가능하다.
- 별도로 설정하지 않는 경우, 컴포넌트에서 접근한 값이 변경되었을 때 리랜더링이 발생한다(기본 값).
- all
로 설정할 경우 쿼리의 어떤 프로퍼티가 변경되든 컴포넌트가 리랜더링된다.
const {
data,
error,
isError,
isIdle,
isPending,
isPaused,
isSuccess,
failureCount,
failureReason,
mutate,
mutateAsync,
reset,
status,
submittedAt,
variables,
} = useMutation({
mutationFn,
gcTime,
mutationKey,
networkMode,
onError,
onMutate,
onSettled,
onSuccess,
retry,
retryDelay,
throwOnError,
meta,
})
mutate(variables, {
onError,
onSettled,
onSuccess,
})
GET
하는 경우에는 useQuery
를 사용한다.POST
, PUT
, PATCH
, DELETE
와 같이 수정하고자 하는 경우에는 useMutation
을 사용한다.const mutation = useMutation({
mutationFn: createTodo,
onMutate() {
/* ... */
},
onSuccess(data) {
console.log(data);
},
onError(err) {
console.log(err);
},
onSettled() {
/* ... */
},
});
const onCreateTodo = (e) => {
e.preventDefault();
mutate({ title });
};
mutation 객체
의 mutate 메서드를 이용해서 요청 함수를 호출할 수 있다.onSuccess
, onError
메서드를 통해 성공 했을 시, 실패 했을 시 response 데이터를 핸들링 할 수 있다.onMutate
는 mutation 함수가 실행되기 전에 실행되고, mutation 함수가 받을 동일한 변수가 전달된다.onSettled
는 try…catch…finally 구문의 finally 처럼 요청의 성공 여부에 상관 없이 마지막에 실행된다.const mutation = useMutation(addTodo);
try {
const todo = await mutation.mutateAsync(todo);
console.log(todo);
} catch (error) {
console.error(error);
} finally {
console.log("done");
}
promise
형태의 response가 필요한 경우라면 mutateAsync
를 사용해서 얻어올 수 있다.useQuery
useQuery의 제네릭은
4개
이다.
- TQueryFnData
- useQuery로 실행하는 query function의
실행 결과
의 타입을 지정하는 제네릭 타입이다.- TError
- query function의
error 형식
을 정하는 제네릭 타입이다.- TData
- useQuery의
data에 담기는 실질적인 데이터
의 타입을 의미한다.- TQueryKey
- useQuery의 첫 번째 인자
queryKey
의 타입을 명시적으로 지정해주는 제네릭 타입이다.
useMutation
useMutation의 제네릭도 마찬가지로
4개
이다.
- TData
- useMutation에 넘겨준 mutation function의
실행 결과
의 타입을 지정하는 제네릭 타입이다.
data
와onSuccess
의 인자 타입으로 활용된다.- TError
- useMutation에 넘겨준 mutation function의
error 형식
을 정하는 제네릭 타입이다.- TVariables
mutate 함수
에 전달할 인자를 지정하는 제네릭 타입이다.
onSuccess
,onError
, onMutate, onSettled 인자의 타입으로 활용된다.- TContext
- mutate function을 실행하기 전에 수행하는 onMutate 함수의
return 값
을 지정하는 제네릭 타입이다.
- onMutate의 결과 값의 타입을
onSuccess
,onError
,onSettled
에서 활용하려면 해당 타입을 지정해야 한다.
useQuery | TanStack Query Docs
useMutation | TanStack Query Docs
Dependent Queries | TanStack Query Docs
Queries | TanStack Query Docs
QueryClientProvider | TanStack Query Docs
QueryClient | TanStack Query Docs
GitHub - ssi02014/react-query-tutorial: 😃 TanStack Query(aka. react query) 에서 자주 사용되는 개념 정리