[React] react-query (2) - Queries, Mutations, Query Invalidation
Defaults
cached data
- 더 이상 useQuery, useInfiniteQuery의 활성 인스턴스가 없는 쿼리 결과 또는 쿼리 옵저버들은 inactive 상태가 되고, 나중에 다시 사 용될 때 캐시에 남아 있게 된다.
- 기본적으로 inactive한 쿼리는 5분 후에 GC 처리 된다. 즉 default cacheTime이 5분이라는 의미로, 변경하고 싶다면 cacheTime 옵션에 변경하고자 하는 시간을 설정하면 된다.
- default cacheTime: 5분 (1000 * 60 * 5 ms)
stale data
- useQuery 또는 useInfiniteQuery 훅을 사용해서 캐시된 데이터는 기본적으로 stale한 데이터로 취급한다.
- 위와 같은 default option은 전체(global) 쿼리 또는 하나의 쿼리에 대해서 staleTime 옵션을 설정하면 변경할 수 있으며, staleTime이 길다는 건 그 시간 만큼 refetch를 하지 않는다는 것을 의미한다.
- staleTime 기본값은 0ms이다.
- default staleTime: 0
refetch
-Stale 쿼리는 아래와 같은 상황에서 자동으로 refetch된다.
1) 쿼리의 새로운 인스턴스가 마운트됐을 때
2) 윈도우가 refocused 됐을 때
3) 네트워크가 재연결됐을 때
4) refetch 간격에 대한 옵션이 별도로 설정되어 있을 때
위와 같은 refetch를 제어하고 싶다면 각각 refetchOnMount, refetchOnWindowFocus, refetchOnReconnect, refetchInterval 옵션을 변경하면 된다.
retry
- 실패한 쿼리는 에러가 캡쳐되고 화면에 표시되기 전에 exponential backoff 지연을 발생시키면서 자동으로 3번까지 다시 시도한다.
- 기본으로 설정되어 있는 재시도 횟수를 변경하고 싶다면 retry 옵션을, exponential backoff 지연과 관련된 기능을 변경하고 싶다면 retryDelay 옵션을 변경하면 된다.
3 core concept
Queries
Query는 서버의 데이터를 받아 오기 위해 사용하며, 공식 문서에서는 만약 서버에 있는 데이터를 수정하거나 삭제하는 게 목적이라면 Queries가 아닌 Mutations을 사용하는 것을 추천하고 있다.
- 컴포넌트나 커스텀 훅에 쿼리(서버에서 받아 온 데이터)를 구독하고 싶다면 해당 쿼리만을 위한 유니크한 쿼리 키와 Promise를 반환하는 함수와 함께 useQuery 훅을 사용하면 됨
- 유니크한 쿼리 키는 데이터를 refetch하고, cache하고, 어플리케이션 전체에 쿼리를 공유하는 데 사용됨
example
import { useQuery } from 'react-query'
function App() {
const info = useQuery('todos', fetchTodoList)
}
위의 예시에서 useQuery 훅의 첫 번째 인자인 'todos'는 unique query key 이며, fetchTodoList는 Promise를 반환하는 fetch 함수임
state
- isLoading(boolean) or status === 'loading' : 쿼리에 아직 받아 온 데이터가 없으며 받아 오는 중인 상태
- isError(boolean) or status === 'error' : 쿼리가 에러를 마주침
- isSuccess(boolean) or status === 'success' : 데이터를 받아 오는 데 성공했으며 받아 온 데이터를 사용할 수 있는 상태
- isIdle(boolean) or status === 'idle' : 쿼리가 불가능한 상태
property
- error : 만약 현재 쿼리의 상태가 isError(status === 'error')라면, error 프로퍼티를 통해 error를 활용할 수 있음
- data : 만약 현재 쿼리의 상태가 isSuccess(status === 'success')라면, data 프로퍼티를 통해 data를 활용할 수 있음
- isFetching : background refetch를 포함하여 쿼리가 데이터를 받아 오고 있는 상태라면 어느 때이든 isFetching 프로퍼티의 값은 true임
function Todos() {
const { isLoading, isError, status, data, error } = useQuery('todos', fetchTodoList)
// isLoading를 status === 'loading'으로 대체할 수 있다.
if (isLoading) {
return <span>Loading...</span>
}
// isError를 status === 'error'로 대체할 수 있다.
if (isError) {
return <span>Error: {error.message}</span>
}
// 여기서 isSuccess의 값이 true 라는 것 또는 status === 'success'라는 것을 추정할 수 있다.
// 또한 else 로직도 사용할 수 있다.
return (
<ul>
{data.map(todo => (
<li key={todo.id}>{todo.title}</li>
))}
</ul>
)
}
throwing error
react-query가 에러가 발생했는지 판명할 수 있으려면 반드시 query function이 에러를 뱉어내야 한다. axios나 graphql-request 같은 라이브러리들은 HTTP 통신에 실패한 경우 자동으로 에러를 던지지만 fetch API 등 자동으로 에러를 던지지 않는 것도 있다. 이런 경우에는 직접 에러를 던지도록 추가로 작성해야 한다. 아래의 예시는 공식문서에 나와 있는 직접 에러를 던지도록 하는 간단한 작성 방법이다.
useQuery(['todos', todoId], async () => {
const response = await fetch('/todos/' + todoId)
if (!response.ok) {
throw new Error('Network response was not ok')
}
return response.json()
})
파라미터를 객체로 나타내기
useQuery의 파라미터에 들어가는 queryKey, query function, config를 아래와 같이 key-value 형태의 객체로도 작성할 수 있다.
import { useQuery } from 'react-query'
useQuery({
queryKey: ['todo', 7],
queryFn: fetchTodo,
...config,
})
동적으로 여러 개의 useQuery 사용하기
만약 실행시켜야 하는 쿼리의 개수가 렌더링 할 때마다 동적으로 변한다면 useQueries 훅을 사용하면 된다. useQueries는 쿼리 options의 객체를 요소로 담고 있는 배열을 파라미터로 받아서 각 쿼리의 결과를 요소로 담고 있는 배열을 반환한다.
function App({ users }) {
const userQueries = useQueries(
users.map(user => {
return {
queryKey: ['user', user.id],
queryFn: () => fetchUserById(user.id),
}
})
)
}
Mutations
query가 서버의 데이터를 조회하는 데 사용된다면, mutations는 서버에 데이터를 생성하거나, 기존에 있는 데이터를 변경하거나 삭제할 때 사용한다. 사용 방법은 useMutation 훅을 사용하는 것이다.
state
mutation은 다음의 네 가지 상태 중 하나의 상태를 가진다.
- isIdle or status === 'idle'
- isLoading or startus === 'loading'
- isError or status === 'error'
- isSuccess or status === 'success'
property
- error
- data
⚠️
mutate 함수는 비동기 함수
Query Invalidation