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'

queryCache.invalidateQueries(queryKey)

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: {
    suspense,
  },
  queries: {
    ...queries  
  },
  mutations: {
    ...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:

We made a custom demo for .
No really. Click here to check it out.

Before:

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

{
  isFetching
  ? "Fetching"
  : data
}

After:

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:

src/index.js

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 (
        <React.Fragment>
            <h1>Fast Recipes</h1>
            <hr/>

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

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.

src/components/Recipe.jsx

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}],
        fetchRecipe
    );

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

The bolded code indicates the updated parts of the code.

Next, we update the Recipes component.

src/components/Recipes.jsx

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 (
        <div>
            <h2>Recipes List <br/>
                {recipesQuery.isFetching
                    ? "Loading"
                    : <Button onClick={() => {
                        queryCache.invalidateQueries("Recipes")
                    }}>
                        Refresh Recipes
                    </Button>
                }
            </h2>
            {recipesQuery.data.map(Recipe => (
                <p key={Recipe.title}>
                    {Recipe.title}
                    <Button
                        onClick={() => {
                            // Prefetch the Recipe query
                            queryCache.prefetchQuery(["Recipe", {id: Recipe.id}], fetchRecipe);
                            setActiveRecipe(Recipe.id);
                        }}
                    >
                        Load Recipe
                    </Button>{" "}
                </p>
            ))}
        </div>
    );
}

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

Conclusion

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 ❤ .

Full visibility into production React apps

Debugging React applications can be difficult, especially when users experience issues that are difficult to reproduce. If you’re interested in monitoring and tracking Redux state, automatically surfacing JavaScript errors, and tracking slow network requests and component load time, try LogRocket.

LogRocket is like a DVR for web apps, recording literally everything that happens on your React app. Instead of guessing why problems happen, you can aggregate and report on what state your application was in when an issue occurred. LogRocket also monitors your app's performance, reporting with metrics like client CPU load, client memory usage, and more.

The LogRocket Redux middleware package adds an extra layer of visibility into your user sessions. LogRocket logs all actions and state from your Redux stores.

Modernize how you debug your React apps — .

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

Leave a Reply