Mais sobre React Query
Desativando/Pausando Queries
Sempre que você quiser desativar uma query automaticamente enquanto ela estiver rodando, você pode usar a opção enabled=false
no objeto config dentro da hook useQuery
.
Quando enabled
é false:
- Se a query guardou os dados no cache:
- A query é inicializada no
status==='success'
ouisSuccess
.
- Se a query não tem cache nenhum dos dados:
- A query inicia em
status==='idle'
ouisIdle
.
- A query não é automaticamente disparada quando o componente é renderizado.
- A query não faz refetch em segundo plano quando novas instâncias aparecem.
- A query vai ignorar a propriedade
invalidateQueries
e a chamada arefetchQueries
é feita resultando em uma nova execução da query. refetch
pode ser usado pra disparar a execução da query manualmente.
function Pokemons() {
const {
isIdle,
isLoading,
isError,
data,
error,
refetch,
isFetching,
} = useQuery('pokemons', fetchPokemonList, {
enabled: false,
})
return (
<>
<button onClick={() => refetch()}>Fetch Pokemons</button>
{isIdle ? (
'Not ready...'
) : isLoading ? (
<span>Loading...</span>
) : isError ? (
<span>Error: {error.message}</span>
) : (
<>
<ul>
{data.map(pokemon => (
<li key={pokemon.id}>{pokemon.title}</li>
))}
</ul>
<div>{isFetching ? 'Fetching...' : null}</div>
</>
)}
</>
)
}
Query Retries
Quando uma query function falha, o React Query tem como comportamento padrão tentar de novo executar essa query 3 vezes por padrão. Mas você pode personalizar esse comportamento:
- Definindo o valor de
retry
prafalse
, desativando assim esse default de tentativas após o erro. - Definindo algum valor numérico para
retry
para que seja feita x tentativas após a falha. - Definido o valor de
retry
comotrue
pra tentar infinitamente. - Inventar moda criando uma arrow function dentro de
retry
seguindo o seguinte exemplo como base:
import { useQuery } from 'react-query'
const result = useQuery(['todos', 1], fetchTodoListPage, {
retry:(failureCount, error) =>{console.log('olha as coisa q eu faço')},
})
Delay
Pode definir até um delay global antes de tentar novamente, olha:
import { QueryCache, QueryClient, QueryClientProvider } from 'react-query'
const queryClient = new QueryClient({
defaultOptions: {
queries: {
retryDelay: attemptIndex => Math.min(1000 * 2 ** attemptIndex, 30000),
},
},
})
function App() {
return <QueryClientProvider client={queryClient}>...</QueryClientProvider>
}
Ou por query também:
const result = useQuery('todos', fetchTodoList, {
retryDelay: 1000,
})
Prefetching
Se você for uma pessoa sortuda, saberá quais dados os usuários precisarão antes mesmo de disparar explicitamente a chamada a esses dados. Quando isso acontece a lib React Query tem a função prefetchQuery
ao nosso dispor. Veja só:
const prefetchTodos = async () => {
const queryClient = useQueryClient()
// Os resultados dessa query serão guardados no cache
await queryClient.prefetchQuery('todos', fetchTodos)
}
- Se os dados dessa query já estão em cache e não foram invalidados, os dados não serão buscados novamente.
- Se a prop
staleTime
é passada por ex.prefetchQuery('todos',fn,{staleTime:5000})
e os dados são mais antigos que esse tempo indicado, então a query será executada. - Se nenhuma instância de
queryClient
aparecer pra query pré-carregada, então ela será deletada e o cache será limpado num tempo especificado porcacheTime
.
Fazendo na mão
Caso você já tenha os dados da sua query disponíveis, não é necessário fazer esse prefetch. Você pode usar a função setQueryData
de queryClient
diretamente pra adicionar ou atualizar o cache pela key da query.
const queryClient = useQueryClient()
queryClient.setQueryData('todos', todos)
Invalidando querys
Esperar queries se tornarem obsoletas (stale state) antes de serem buscadas novamente nem sempre dá certo, especialmente quando você sabe que esses dados estão desatualizados devido a algo que o usuário tenha modificado. Pra esse propósito, o QueryClient
tem a função invalidateQueries
que deixa você marcar quais queries podem dar esse problema para que o refetch delas seja feito.
const queryClient = useQueryClient()
// Invalidate every query in the cache
queryClient.invalidateQueries()
// Invalidate every query with a key that starts with `todos`
queryClient.invalidateQueries('todos')
Quando uma query é invalidada com o invalidateQueries
, duas coisas acontecem:
- Ela é marcada como
stale
no state. Esse state sobrescreve qualquer configuração destaleTime
a ser usadas na hookuseQuery
e suas semelhantes. - Se a query estiver sendo renderizada via
useQuery
ou semelhantes, ela também fará o refetch delas em segundo plano.
Query Matching com invalidateQueries
Quando usamos APIs como invalidateQueries
e removeQueries
(ou outras que suportarem matching parcial de query), você pode marcar múltiplas queries pelo seu prefixo, ou marcar uma especificamente uma determinada query. Vamos supor que você tenha 10 queries que iniciam em getAllBy
. Se filtrarmos por esse prefixo invalidaremos todas as 10. Veja abaixo o exemplo:
import { useQuery, useQueryClient } from 'react-query'
const queryClient = useQueryClient()
queryClient.invalidateQueries('getAllBy');
//todas as queries abaixo serão invalidadas
const todoListQuery = useQuery('getAllById', fetchTodoList)
const todoListQuery = useQuery(['getAllByPage', { page: 1 }], fetchTodoList)
const todoListQuery = useQuery(['getAllByName', { name: 'DevDoidão' }], fetchTodoList)
E você ainda pode invalidar queries usando variáveis específicas de acordo com o que você passa na key da query. Veja:
queryClient.invalidateQueries(['todos', { type: 'done' }])
// Vai ser invalidada
const todoListQuery = useQuery(['todos', { type: 'done' }], fetchTodoList)
// Não vai ser invalidada
const todoListQuery = useQuery('todos', fetchTodoList)
A função invalidateQueries
é muito flexível, se você quiser restringir a uma única e exclusiva invalidação basta passar a prop exact:true
no método. Veja:
queryClient.invalidateQueries('todos', { exact: true })
// Vai ser invalidada
const todoListQuery = useQuery(['todos'], fetchTodoList)
// Não vai ser invalidada
const todoListQuery = useQuery(['todos', { type: 'done' }], fetchTodoList)
Se quiser inventar alguma validação antes de ver quais queries você deseja invalidar, basta usar a prop predicate
e definir sua lógica via arrow function. Veja:
queryClient.invalidateQueries({
predicate: query =>
query.queryKey[0] === 'todos' && query.queryKey[1]?.version >= 10,
})
// Vai ser invalidada
const todoListQuery = useQuery(['todos', { version: 20 }], fetchTodoList)
// Vai ser invalidada
const todoListQuery = useQuery(['todos', { version: 10 }], fetchTodoList)
// Não vai ser invalidada
const todoListQuery = useQuery(['todos', { version: 5 }], fetchTodoList)
Por hoje é só, me siga para mais dicas!!