Things I learned while using React Query - Part 2
Tags: react, reactquery, state
Published: 2021-03-08 08:14:15

This blog post is the second in a series:

1. [Things I learned while using React Query - Part 1](https://codemachine.dev/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.

```jsx
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

```jsx
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

```jsx
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:

```jsx
// 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})
}
```