Things I learned while using React Query - Part 2

marinvirdol

Marin Virdol

Posted on March 8, 2021

Things I learned while using React Query - Part 2

This blog post is the second in a series:

  1. Things I learned while using React Query - Part 1
  2. 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',
    },
  },
})
Enter fullscreen mode Exit fullscreen mode

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()
  }
}
Enter fullscreen mode Exit fullscreen mode

And second, we need to tell React Query about this function

const queryClient = new QueryClient({
  defaultOptions: {
    queries: {
      queryFn: createQueryFn('https://api_base_url'),
    },
  },
})
Enter fullscreen mode Exit fullscreen mode

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})
}
Enter fullscreen mode Exit fullscreen mode
💖 💪 🙅 🚩
marinvirdol
Marin Virdol

Posted on March 8, 2021

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related