Query invalidation
쿼리가 refetch되기전 'stale'상태가 되는 것이 항상 있는 일은 아니다. 특히 사용자가 작업을 완료해서 쿼리 데이터가 더이상 유효하지 않은 경우에도 작동하지 않을 수 있다. 이를 위해 QueryClient에는 invalidateQueries 메서드가 존재한다. 이 메서드를 사용하면 쿼리를 적절하게 "stale" 상태로 표시하고, 필요한 경우 refetch할 수 있다.
// Invalidate every query in the cache
queryClient.invalidateQueries()
// Invalidate every query with a key that starts with `todos`
queryClient.invalidateQueries({ queryKey: ['todos'] })
쿼리가 'invalidateQueries'에 의해 'invalidated'되었을때 두가지 일이 발생한다.
- 쿼리는 stale라벨이 붙는다. 이 stale라벨은 'useQuery'나 관련된 다른 메서드의 'staleTime'옵션을 덮어쓴다.
(staleTime에 의해 refetch되지 않는다는소리인가..?) - 만약 쿼리가 'useQuery'나 관련된 다른 메서드에 의해 렌더되고있다면 이 쿼리는 background에서 refetch될 것이다.
Query Matching with 'invatidateQueries'
'invalidateQueries' 혹은 'removeQueries'와 같은 API를 사용할때 쿼리키를 어떻게 사용하느냐에 따라 다양한 'invalidate'양상이 일어난다.
import { useQuery, useQueryClient } from '@tanstack/react-query'
// Get QueryClient from the context
const queryClient = useQueryClient()
queryClient.invalidateQueries({ queryKey: ['todos'] })
// Both queries below will be invalidated
const todoListQuery = useQuery({
queryKey: ['todos'],
queryFn: fetchTodoList,
})
const todoListQuery = useQuery({
queryKey: ['todos', { page: 1 }],
queryFn: fetchTodoList,
})
queryClient.invalidateQueries({
queryKey: ['todos', { type: 'done' }],
})
// The query below will be invalidated
const todoListQuery = useQuery({
queryKey: ['todos', { type: 'done' }],
queryFn: fetchTodoList,
})
// However, the following query below will NOT be invalidated
const todoListQuery = useQuery({
queryKey: ['todos'],
queryFn: fetchTodoList,
})
만약 ['todos']란 접두사로 시작하는 여러개의 쿼리키가 있는데, 그중 여러개 모두 invalidate되기를 원하지 않고, ['todos']만
invalidate되기를 원한다면, 'exact'옵션을 true로 설정함으로써 해결할 수 있다.
queryClient.invalidateQueries({
queryKey: ['todos'],
exact: true,
})
// The query below will be invalidated
const todoListQuery = useQuery({
queryKey: ['todos'],
queryFn: fetchTodoList,
})
// However, the following query below will NOT be invalidated
const todoListQuery = useQuery({
queryKey: ['todos', { type: 'done' }],
queryFn: fetchTodoList,
})
조금더 상세히 작업하려면 'predicate'함수를 이용하면된다.
'predicate'함수를 이용하여 true 혹은 false여부에 따라 invalidate여부를 결정하도록 코드를 짠 예시가 아래에 있다.
queryClient.invalidateQueries({
predicate: (query) =>
query.queryKey[0] === 'todos' && query.queryKey[1]?.version >= 10,
})
// The query below will be invalidated
const todoListQuery = useQuery({
queryKey: ['todos', { version: 20 }],
queryFn: fetchTodoList,
})
// The query below will be invalidated
const todoListQuery = useQuery({
queryKey: ['todos', { version: 10 }],
queryFn: fetchTodoList,
})
// However, the following query below will NOT be invalidated
const todoListQuery = useQuery({
queryKey: ['todos', { version: 5 }],
queryFn: fetchTodoList,
})
Edit on GitHub
Invalidations from Mutations
쿼리를 invalidate하는 것은 전투의 절반이고, 쿼리를 언제 invalidate할것인가에 관한 나머지 절반의 전투가 남아있다.
일반적으로 앱에서 mutation 성공하면, 해당 mutation과 관련된 쿼리가 존재할 가능성이 매우 높다. 이 경우, 이전 쿼리 결과가 변경되었기 때문에 해당 쿼리를 무효화하고 다시 가져와야 할 필요가 있다.
따라서 앱에서 새로운 mutation이 수행될 때마다, 이와 관련된 모든 쿼리를 검사하고 적절하게 무효화(invalidate)하고 재요청(refetch)해야 한다. 이렇게 함으로써 앱에서 항상 최신 데이터를 유지할 수 있다.
예를들어 새로운 todo를 추가하는 코드를 보자.
const mutation = useMutation({ mutationFn: postTodo })
이 코드가 성공하였을때 기존의 쿼리를 invalidate하고 refetch해야한다. 이는 'useMutation'함수의 'onSuccess'옵션을 이용해 해결할 수 있다.
import { useMutation, useQueryClient } from '@tanstack/react-query'
const queryClient = useQueryClient()
// When this mutation succeeds, invalidate any queries with the `todos` or `reminders` query key
const mutation = useMutation({
mutationFn: addTodo,
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['todos'] })
queryClient.invalidateQueries({ queryKey: ['reminders'] })
},
})
Updates from Mutation Responses
서버에 존재하는 데이터를 업데이트하는 mutation을 다룰때 일반적으로 새로운 객체가 뮤테이션의 응답으로 자동 반환된다. 이미 가지고 있는 데이터를 가져오기위해 네트워크 호출을 하는 대신, mutation함수에서 반환된 객체를 활용하여 기존 쿼리를 즉시 새롭게 업데이트할 수 있다. 이를 위해서는 setQueryData메서드를 사용하면 된다.
const queryClient = useQueryClient()
const mutation = useMutation(editTodo, {
onSuccess: data => {
queryClient.setQueryData(['todo', { id: 5 }], data)
}
})
mutation.mutate({
id: 5,
name: 'Do the laundry',
})
// The query below will be updated with the response from the
// successful mutation
const { status, data, error } = useQuery(['todo', { id: 5 }], fetchTodoById)
onsuccess상태에서 재사용가능한 로직을 mutation과 결합하고자 할 때, 다음과 같이 커스텀 훅을 만들 수 있다.
const useMutateTodo = () => {
const queryClient = useQueryClient()
return useMutation(editTodo, {
// Notice the second argument is the variables object that the `mutate` function receives
onSuccess: (data, variables) => {
queryClient.setQueryData(['todo', { id: variables.id }], data)
},
})
}
'React-Query' 카테고리의 다른 글
Guides&Concepts_(Mutation) (0) | 2023.04.24 |
---|---|
Guides&Concepts_(Placeholder Query Data & Prefetching) (0) | 2023.04.24 |
Guides&Concepts_(Initial Query Data) (0) | 2023.04.24 |
Guides&Concepts_(Infinite Queries) (0) | 2023.04.23 |
Guides&Concepts_(Query Retries & Paginated Queries) (0) | 2023.04.22 |