Things I learned while using React Query - Part 2
Marin Virdol
Posted on March 8, 2021
This blog post is the second in a series:
- Things I learned while using React Query - Part 1
- Things I learned while using React Query - Part 2 (this post)
Disable some of the defaults while in development
React Query comes with some aggressive defaults that are useful in production
but not that much while developing.
For example, by default, a refetch happens in the background on window focus to
keep the user as up to date as possible with the server. In development you
really don't need to sync with the server so often.
The same goes for the automatic retry behaviour when the query fails. Having no
retry for queries that fail is perfectly acceptable in development and it will
improve your development speed.
I recommend that you disable these two defaults at the level of the query
client. The advantage of doing it here is that you do it in only one place and
you don't need to worry about the other queries in your app.
const queryClient = new QueryClient({
defaultOptions: {
queries: {
retry: process.env.NODE_ENV === 'production',
refetchOnWindowFocus: process.env.NODE_ENV === 'production',
},
},
})
Configure staleTime
based on your needs
If you know that a certain query doesn't change often, probably you should
change the staleTime
from the default value of zero, to a value that best fits
your needs for that specific query.
Use the enabled
option to create dependent queries or to disable/enable a query
I've seen many people having a hard time running a query conditionally. Since
hooks don't work with if statements, React Query provides the enabled
configuration option exactly for this. You can disable/enable a specific query
by providing true
or false
to the enabled
option.
Another useful features that comes with the enabled option is the ability to
create dependent queries. You fetch data in one query and the second query runs
only after the first one successfully completes.
Treat query keys like a dependency array, useEffect
style
React Query does the cache management based on your query keys which means that
your query keys uniquely describe a piece of data in your application. These
query keys can be simple string values, complex nested objects or array of
strings and complex nested objects.
Many of your fetching functions will have dynamic route parameters or query
parameters. Think of the situations when you want to fetch a resource based on
its id or when you are doing server side pagination or filtering.
Knowing this, it's a good idea when designing your query keys, to treat them
like a dependency array, just like you do with your useEffect
hooks. The rule
of thumb is to add to the query key any variable that your fetch function
depends on.
The benefit of doing this is that React Query will automatically trigger a
refetch whenever the query key changes and the synchronisation with the server
just happens.
Create custom hooks
A good practice is to wrap your useQuery
hook calls in your own custom hook.
Functionality wise there is no added benefit, but there are a few developer
benefits.
- First, we separate our data fetching from the UI
- Second, we can be sure that we are NOT using different query keys for the same data
- Lastly, if we need to tweak some settings for a specific query, or add some data transformation, we do that in only one place
Don't be afraid to use your hook in every component you need
If you need the same piece of data across your application in multiple
components, don't be afraid to use your custom hook (or the useQuery
hook with
the same query key) in multiple components.
React Query automatically de-duplicates queries based on the query key,
therefore you can be sure there will not be more than one request per query key.
Use a default query function
To make things even more simpler, you can share the same fetch functionality for
queries throughout your application by creating a default query function.
As I told you before, many of your fetching functions will have dynamic route
parameters or query parameters. This means that we can create a default query
function that we can use for all of our queries.
There are two steps we need to do: create the general fetch function and specify
to React Query that we are going to use a default query function and which is
the function we want to use.
First, let's create the general function
function createQueryFn(baseUrl: string): QueryFunction {
return async ({queryKey}) => {
const path =
typeof queryKey === 'string' ? queryKey : queryKey[0] + qs(queryKey[1])
const res = await fetch(baseUrl + path)
if (!res.ok) throw new Error(await res.json())
return res.json()
}
}
And second, we need to tell React Query about this function
const queryClient = new QueryClient({
defaultOptions: {
queries: {
queryFn: createQueryFn('https://api_base_url'),
},
},
})
Once this is done, you can use the useQuery
hook in the following ways:
// query with simple query key as string
export function useMovies() {
// a GET request will be fired to https://api_base_url/api/movies
return useQuery('/api/movies')
}
// OR
// query with complex query key as object
export function useMovies({page, pageSize, searchTerm}) {
// a GET request will be fired to
// https://api_base_url/api/movies?page=0&pageSize=10&searchTerm=matrix
return useQuery('/api/movies', {page, pageSize, searchTerm})
}
Posted on March 8, 2021
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.