Hamburger Icon
  • Labs icon Lab
  • Core Tech
Labs

Guided: State Management with React-Query

State management is a notoriously tricky problem to solve in event-driven environments such as the ones that you find in client-side web applications. Asynchronous state management can be even trickier! Many applications end up developing their own frameworks and tightly coupled solutions to handle many of the problems that arise from syncing data between client and server. Enter react-query. The open source, react-query library by TanStack is an opinionated, asynchronous state management library for React that will help save you from worrying about many of the problems that are prevalent in client-side web applications that handle asynchronous operations.

Labs

Path Info

Level
Clock icon Intermediate
Duration
Clock icon 52m
Published
Clock icon Jul 18, 2024

Contact sales

By filling out this form and clicking submit, you acknowledge our privacy policy.

Table of Contents

  1. Challenge

    ### Introduction

    In this lab you are going to learn the ins and outs of asynchronous state management in React with the react-query (also known as the “TanStack Query”) library. The react-query library (full NPM library name @tanstack/react-query) is an asynchronous state management library for React. The goal of this library is to provide a simple and declarative API for managing all things asynchronous state management and data fetching. There are many state management libraries out there for React – but most of these are really focused on managing state that always exists in one place – the client. Many state management libraries out there are really good at synchronous, client-side state management but are not as well-suited for solving the problems that come with asynchronous state management.

    Asynchronous state management is that portion of state management that is really all about syncing state between client and server. For client-side React applications, managing this synchronization of state between client and server often results in tedious amounts of boilerplate code, spaghetti code, manual re-fetching of data, etc. Often, developers must write their own specialized frameworks all around this portion of state management so that the client can ensure it is up to date with the server at all times and vice versa. In this lab, you are going to learn about how the react-query library solves these problems as you look into the following topics:

    • Fetching data with queries

    • Pausing and retrying queries

    • Query invalidation and re-fetching data

    • Changing server state with mutations

    This lab will guide you into a full understanding of what react-query calls the Query - a mechanism for abstracting away a lot of the boilerplate code that comes with fetching, parsing and syncing data between the client and server. You will also learn some of the most common use cases for queries along the way. From there you will dive deep into mutations and how you can ensure the server is up to date with data from the client. You'll touch on all of these topics as you work on a simple coffee inventory application.

    info> Note: The solution directory contains the final solution. Feel free to view the files in that directory should you get stuck!

  2. Challenge

    ### Fetching Data with Queries

    When it comes to asynchronous state management, first you need some state to work with! That means we need to fetch data. This is where the story begins and this is where react-query provides instant benefit to its users. Most of the react-query library revolves around an entity known as a Query.

    So, what is a query? Straight from the react-query docs, a query is, “... a declarative dependency on an asynchronous data source that is tied to a unique key.” The Query is the react-query mechanism of abstraction when it comes to fetching data from a remote server.

    Here is a very simple example of a Query using react-query.

    
    {  
    
      queryKey: [“coffees”], 
    
      queryFn: fetchCoffeesFn 
    
    } 
    

    Each query consists of, at a base level, a query key and a query function. The query key is used by the react-query library a variety of different actions around data fetching and ensures the uniqueness of an asynchronous data source.

    The query function is a function that you provide that performs the actual action fetching of the data.

    In this step of the lab, you’ll learn how to write your own query, start using your query with the useQuery hook, and handle the different states of your query. Queries are created via passing simple objects to the useQuery hook provided by react-query. This simple, declarative API then gives you back a query object that can be used for templating in your React components. The query returned by useQuery is an object that exposes a number of fields for use in your React component. In this task, we are concerned with handling one of the different query states that are available. Each query returned by useQuery exposes a number of different, primary query states. These include:

    • Pending
    • Error
    • Success

    Pending means that your query has been executed and is waiting for a response from the server. The error state means that an error occurred with your query and the success state means that your query has executed successfully and is populated with data from the server. With the error and success query states, queries are letting you know that other, related data fields are populated on the query. For example, if the isError field is set to true on the query than you can be assured that the error field on the query is populated with an error object that details what went wrong with the query. Well done! You now have a firm grasp of the foundations around the Query - a core concept of the react-query library. Once you understand how to create queries and use them, you’re well on your way to understanding how to fully use the react-query library to your benefit.

    In the next step, you’ll learn more advanced techniques around using queries including pausing and retrying queries.

  3. Challenge

    ### Pausing and Retrying Queries

    In this step, you’re going to build on your basic understanding of queries by learning how to configure them. Out of the box, the react-query library comes pre-configured with a number of defaults regarding queries. For example, queries will automatically refresh when the window is refocused (when the user switches browser tabs) or when the query key changes, as seen in the last step.

    But what if you have a query that you always want to manually fire. For example, there is a common use case where you need to fetch some data if and only if the user clicks on a button. Well in this case you want to “pause” or “disable” a query. In this step you'll learn how to configure a query for this functionality. You'll also learn how to configure a query when it comes to query retries. If you want to pause or otherwise disable a query, you can use the enabled flag that is available on the query object that is passed to the useQuery hook. By default, the enabled flag is set to true. But if you don’t want a query automatically running, you can disable the query by setting enabled to false. Note that, by disabling a query, you must manually call the refetch function that is available off of the query. Disabling a query means that you are opting out of most of the great features around queries that react-query has to offer. Most of the time, you don’t really want to disable a query. And, if you do, you really only want to do it for a certain period of time. This is where the concept of a “lazy query” comes in. In simplistic terms, a lazy query is just a query where you don’t want the initial execution of the query to fire off. Instead you want your query to fire off only after a certain initial set of criteria is met. The react-query library also lets you customize the default retry logic that is applicable to each query. By default, queries will retry three times. To change this, you can alter the retry field on the object passed to the useQuery hook. You can set retry to a boolean, a number, or a function. Setting the retry field to false means the query will not retry on failure. Setting the field to true will cause the query to infinitely retry the request until it succeeds. You can set the retry field to a number to explicitly tell react-query how many times to retry the request. And you can also set this field to a function to retry the request based on some custom logic. The react-query library defaults to three retries per failed query. But the library also has a default for the delay between retries. This library, per industry best practices, defaults to a doubling delay between retries starting at 1000ms (1 second). You can alter this default by setting the retryDelay field. This field can be set to a number ( the number of milliseconds between each retry) or a function that accepts, as its first argument, the current retry attempt. You can then return from this function the number of milliseconds that you want to delay by relative to the current retry attempt – this lets you do exponential backoffs. Well done! You have seen a couple of the ways in which queries can be configured in terms of retries and manual data fetching. There are so many other things to explore with queries but, unfortunately, this lab can’t cover them all. Here are a few other topics that are worth exploring when it comes to queries:

    In the next step, you’ll learn how react-query can help you out with the “other side” of asynchronous state management – updating server state.

  4. Challenge

    ### Changing Server State with Mutations

    So far, you’ve seen how the react-query library can help you with fetching data but there is another side of the coin when it comes to asynchronous state management – updating server state. Within react-query you fetch data from a server using a Query and you mutate data on the server using a Mutation.

    Mutations are generally used to either create, update or delete data on a remote server. Similar to how queries are created by using a useQuery hook, mutations are created via a useMutation hook. The useMutation hook returns a mutation object that can be used to check the current status of the mutation and to execute the mutation. Just like with queries, mutations have their own states. The same primary states exist for mutations. These include: pending, error, and success. Each of these states are exposed through flags on the mutation object itself: isPending, isError, and isSuccess. Whether it be errors, a pending status or a successful result, muatations have you covered with different error states available. But one cool thing that is unique to mutations is the concept of mutation side effects. Mutations offer you the ability to hook into the different stages of a mutation and run a side effect accordingly. This is very useful for things like logging, for example. Available hooks include:

    • onMutation: The mutation is starting.

    • onError: The mutation errored out.

    • onSuccess: The mutation succeeded.

    • onSettled: The mutation is done – whether it failed or succeeded.

    Well done! You’ve now seen both sides of the coin when it comes to asynchronous state management. Mutations are absolutely essential to getting the most out of react-query. This lab cannot touch on all of the use cases and features of react-query when it comes to mutations, but it may be worth your while to check out some more advanced mutation topics.

    In the next step, you’ll learn how react-query can help you with re-fetching data and query invalidation.

  5. Challenge

    ### Query Invalidation

    As we’ve seen, by default, react-query will re-fetch data when it has become stale according to the cache key. But there are times when you (the client) know that a certain piece of data needs to be re-fetched. Maybe you mutated state on the server and so you know you need to re-fetch another piece of data. Or maybe some other, outside piece of state has come in to your app and, based on that state, you know that you need to query for more up-to-date data. It is within these sorts of situations that query invalidation becomes key.

    To invalidate queries and re-fetch data when you need to with fine granularity, you need to use the invalidateQueries function located on the QueryClient itself. In this step, we’ll investigate ways that you can use this function intelligently in your applications. There are sometimes when you will want to invalidate all queries in your application. Maybe here is a core piece of data or configuration that has changed which requires you to start with a fresh state. When this is the case, you will want to call the invalidateQueries function with no arguments passed to it. This will invalidate all queries in the cache. There are also times when you will want to invalidate specific queries. For example, if you add a new resource to the server, it makes sense that you will want to fetch these resources anew elsewhere in your application. When this is the case, you can pass the invalidateQueries function an argument that is an object containing the queryKey field. It is also possible to invalidate queries based on query filters. Going over the entire slew of query filters available is beyond the scope of this lab, but one really useful filter is the fetchStatus filter. It is assigned to queries that are currently being fetched. This filter is useful because you can invalidate the query that is currently being fetched – forcing this query to re-fetch in lieu of updated data available.

  6. Challenge

    ### Conclusion

    Nicely done! You’ve made it to the end of this guided code lab on the react-query asynchronous state management library.

    In summary, you learned:

    • How to use queries to help manage asynchronous data fetching

    • How to pause, retry and invalidate queries

    • How to mutate server state using react-query mutations

    • How to not write your own framework for managing data fetching and other asynchronous state management operations

    We’ve barely scratched the service in terms of the react-query library and what it provides. As follow on learning, I recommend that you learn about some of the more advanced topics within the react-query ecosystem. Here are a just a few:

    From here, you can be confident when it comes to using the react-query library to help you manage asynchronous state management in your React applications. I recommend that you continue your journey learning both React and React state management methodologies by pursuing both video courses and more guided code labs here, at PluralSight.

Zach is currently a Senior Software Engineer at VMware where he uses tools such as Python, Docker, Node, and Angular along with various Machine Learning and Data Science techniques/principles. Prior to his current role, Zach worked on submarine software and has a passion for GIS programming along with open-source software.

What's a lab?

Hands-on Labs are real environments created by industry experts to help you learn. These environments help you gain knowledge and experience, practice without compromising your system, test without risk, destroy without fear, and let you learn from your mistakes. Hands-on Labs: practice your skills before delivering in the real world.

Provided environment for hands-on practice

We will provide the credentials and environment necessary for you to practice right within your browser.

Guided walkthrough

Follow along with the author’s guided walkthrough and build something new in your provided environment!

Did you know?

On average, you retain 75% more of your learning if you get time for practice.