Abdulazeez Abdulazeez Adeshina Software enthusiast, writer, food lover, and hacker.

What’s new in react-query v2

3 min read 1023

What's New In React-Query v2

react-query v2 was released recently, which once again brought about a change in the react-query API along with overall performance improvements. It’s pegged at version 2.5.4 at the time of writing this post, and a comprehensive list of the updates can be found on the changelog.

In this post, we’ll discuss briefly the new changes and rewrite an app built with react-query v1 in a previous article to use the new version.

Moving on, I’ll be discussing these changes in the following sections, but it is essential that you check this article where I talked about react-query and built a sample first.

The idle query state

In the older versions of react-query, dependent queries were expected to start in the success state. A new query state has been introduced and can be used as the default state for dependent queries. As a result, appropriate messages can be displayed based on the state of the query. An example from the changelog is:

const { status } = useQuery(queryKey, queryFunction)

return status == 'idle' ? 'Not ready' : status == 'loading' ? 'Loading data'
: status === 'error'
? error.message
: 'The data'


Formerly queryCache.refetchQueries(queryKey), the new API method re-fetches only active queries, unlike in the previous versions, where all matching queries were re-fetched regardless of their state.

The new global configuration object

The new global configuration object is now sectioned. Sectioning here means that react-query objects from a family are grouped under one sub-object. The new global configuration has three parts:

  1. The shared section, housing the suspense option.
  2. The queries object, housing all query-related options.
  3. The mutations object, housing all mutation-related options.

The new global object looks like this:

const globalConfig = {
  shared: {
  queries: {
  mutations: {

In the course of rewriting the application from the previous version, we’ll restructure our app’s global object to follow the new pattern.

New query status Booleans

From version 2, a query can be assigned to a single variable without having to split them into separate variables. The new query statuses added are:

isLoading, isSuccess, and isError

Here’s an example:


const { data, isFetching } = useQuery("Recipes", fetchRecipes);

  ? "Fetching"
  : data


const recipeQuery = useQuery("Recipes", fetchRecipes);

return recipeQuery.isLoading ? ("Loading recipes") 
  : recipeQuery.isError 
  ? ( recipeQuery.error.message ) 
  : ( <div> recipeQuery.data )

These are some of the highlights of the v2 release. Other updates, mostly bug fixes and minor updates, can be reviewed in the changelog.

Migrating from v1 to v2

In this section, we will be migrating a previously built app from react-query v1 to v2. I assume that you have experience writing JavaScript and React, and using Yarn and Git.

We’ll start off by cloning the GitHub repository and updating react-query:

git clone https://github.com/Youngestdev/react-query-app

After cloning:

cd react-query-app & yarn install & yarn upgrade react-query

In our application, only three components were affected by the new changes. The recipe, recipes and main component, we’ll be reflecting the new changes in them. We’ll start with the main component:


In this component, we’ll reconfigure the global configuration object. Replace the existing code with the block below:

import React, {lazy} from "react";
import ReactDOM from "react-dom";
import {ReactQueryConfigProvider} from "react-query";
import {ReactQueryDevtools} from "react-query-devtools";

const Recipes = lazy(() => import("./components/Recipes"));
const Recipe = lazy(() => import("./components/Recipe"));

const queryConfig = {
    shared: {
        suspense: true
    queries: {
        refetchOnWindowFocus: true


function App() {
    const [activeRecipe, setActiveRecipe] = React.useState(null);

    return (
            <h1>Fast Recipes</h1>

            <ReactQueryConfigProvider config={queryConfig}>
                <React.Suspense fallback={<h1> Loading ...</h1>}>
                    {activeRecipe ? (
                    ) : (
                        <Recipes setActiveRecipe={setActiveRecipe}/>
            <ReactQueryDevtools initailIsOpen={false}/>

const rootElement = document.getElementById("root");
ReactDOM.render(<App/>, rootElement)

The global configuration has been modified as highlighted above. We also added a new option, refetchOnWindowFocus, that re-fetches the rendered query when the browser window is visited.

Next, we’ll update the Recipe component.


In this component, we’ll update the query variables to use the new query status Boolean. Replace the existing code with this:

import React from "react";
import {useQuery} from 'react-query';

import Button from "./Button";

import {fetchRecipe} from "../queries";

export default function Recipe({activeRecipe, setActiveRecipe}) {
    const recipeQuery = useQuery(
        ["Recipe", {id: activeRecipe}],

    return (
            <Button onClick={() => setActiveRecipe(null)}>Back</Button>
                ID: {activeRecipe} {recipeQuery.isFetching ? "Loading Recipe" : null}
            {recipeQuery.data ? (
                    <p>Title: {recipeQuery.data.title}</p>
                    <p>Content: {recipeQuery.data.content}</p>
            ) : null}

The bolded code indicates the updated parts of the code.

Next, we update the Recipes component.


In this component, we’ll update the query handler and render style. We’ll also replace the use of refetchQueries to invalidateQueries.

Replace the existing code with this:

import React from "react";
import {queryCache, useQuery} from "react-query";

import Button from "./Button";

import {fetchRecipe, fetchRecipes} from "../queries";

export default function Recipes({setActiveRecipe}) {
    const recipesQuery = useQuery("Recipes", fetchRecipes);

    return (
            <h2>Recipes List <br/>
                    ? "Loading"
                    : <Button onClick={() => {
                        Refresh Recipes
            {recipesQuery.data.map(Recipe => (
                <p key={Recipe.title}>
                        onClick={() => {
                            // Prefetch the Recipe query
                            queryCache.prefetchQuery(["Recipe", {id: Recipe.id}], fetchRecipe);
                        Load Recipe
                    </Button>{" "}

We have successfully migrated our app from v1 to v2. Our app functions the same way as it was before.

Our Finished App Post-migration


The new updates to react-query are excellent. The addition of new query status booleans makes it easy to display appropriate messages at every query status.

Check here to reference the code snippets used in the new features explanations above. Keep building amazing things, and be sure to keep checking the blog for crispy new posts ❤ .

Cut through the noise of traditional React error reporting with LogRocket

LogRocket is a React analytics solution that shields you from the hundreds of false-positive errors alerts to just a few truly important items. LogRocket tells you the most impactful bugs and UX issues actually impacting users in your React applications. LogRocket automatically aggregates client side errors, React error boundaries, Redux state, slow component load times, JS exceptions, frontend performance metrics, and user interactions. Then LogRocket uses machine learning to notify you of the most impactful problems affecting the most users and provides the context you need to fix it.

Focus on the React bugs that matter — .

Abdulazeez Abdulazeez Adeshina Software enthusiast, writer, food lover, and hacker.

Leave a Reply