Published on

React Query the library 90% of React Apps Should Be Using

Authors

I'm not one for click baity titles, but hopefully you are now reading this and I'm positive that you'll be in for a good treat of information!

Intended Target Audience

If you fall into one of these situations and categories below, this is especially for you!

  • starting a new react project
  • a tech lead exploring ways to simplify your react codebase
  • working on small-medium sized react application
  • currently using Redux or other similar libraries to manage your application state

If you are not in one of the situations or categories above, I still recommend you read along because some of the concepts are applicable to other modern javascript frameworks. When I found out about React Query and the principles behind it, it dramatically changed my perspective on how to create client side web applications. Additionally, it also provided me a richer vocabular to talk about certain aspects of my application,

What is React Query?

In the simplest sense, React Query is a library that provides a set of React hooks. Yes, just a set of React hooks and that's mostly it! More specifically, these set of hooks that React Query provides allow you to very easily manage the "server state" of your clientside web application.

But wait, what do you mean by "server state"? What is server state specifically? and why the need for this specific distinction? Are there other types of state in my app?

These were the thoughts I initially had when I heard React Query's creator, Tanner Linsly, talk about React Query in a presentation he gave (I'll share resources these resources at the end!)

Global State vs Client State vs Server State

Before we dive more into React Query, its important to get some brief history and context of some terms which will make understanding React Query not only easier, but it will gives us insights on why React Query was created.

Before my exposure to React Query, my view of state on the frontend consisted mostly of these two types of states:

  1. component state - data that's local to a component
  2. global state - data that can be shared and passed down nearly everywhere in the app.

and inside these two types of state containers we could store both UI specific data and server derived data server state:

const globalState {
isSideBarMenuOpen: true,
theme: 'dark',
toasts: [],
workspaces: [...],
teams: [...],
users: [...],
todos: [...],
books: [...],
rentals: [...],
subscriptions: [...],
settings: [...]
}

Grouping these two types of data seem harmless and is very easy to do. What if I told you that one of the reasons why many of our tools have gotten complicated is because were treating these types of data the same and storing them together? When really we should be more helpful to look at it like this:

// 1. Client application state
{
isSideBarMenuOpen: true,
theme: 'dark',
toasts: [],
}
// 2. Server State (Things that come back from an API or filesystem)
{
workspaces: [...],
teams: [...],
users: [...],
todos: [...],
books: [...],
rentals: [...],
subscriptions: [...],
settings: [...]
}

We Need Two Different Tools, not 1 Hammer

One of the reasons why some older state management frameworks like Redux require so much boilerplate, and can be complicated to use, is because they treats both client state and server in similiar way with regard to how we access them.


Client State
Non Persistent Temporary, local and may not be persisted across sessions
Synchronous access with synchronous APIs that have near zero latency
Client Owned State the mostly owned by the client application's instance
Relibably Up to Date Given that the state is mostly owned by the client application's instance, we reliably assume the data is up to date
Server State
Remotely Persisted Data location and source of truth of our data is potentially unknown and/or outside our control
Asynchronous data accessed with async APIs that have variable latency
Implied Shared Ownership the data is not just manipulated by our client, but can be read, updated by the server and other clients that interacts with out app
Potentially Out of Date very few guarantees can be made about our server state always being up to date in our app and instead usually rely on just snapshots of our async data

Both of these types of states, which at the end of the day get stored as simple values in our data store, have very different resposibilities and unque set of challenges. When try to store both of the types of data in the same system, we will unavaoidably make tradeoffs in favoring one over the other. This why Redux in my opinion can become a very complicated tool to work with.

For example, server state has its own set of challenges that you don't see in client state state:

  • caching API requests
  • deduping API requests
  • updating data in the background to deal with outdated requests
  • dealing with mutations pagination and incremental fetching

Many global state libraries don't support these, yet we find ourselves doing all of these things with complicated plugins and APIs built on top of the library.

What if we separated client and server state? What would our apps look like?

🚧 NOTE: I don't Redux is a bad tool or was concieved to be "complicated", but rather at the time it was created it really was the best thing around for managing global state in a predictable manner as the industry was still figuring out how best to do this. In fact, Redux and other similar tools paved the way for React Query.

Intro to React Query

React Query is React hooks library that helps you manage only the server state of your application. Below is is an example of fetching data from a service.

import { useQuery } from 'react-query'
import BOOK_API from '....'
function ExampleComponent() {
const { isLoading, error, data: book } = useQuery(bookId, BOOK_API.get(bookId))
)

After the data is retrieved, the great thing about React Query is that it automatically adds this data from your API call into the server state store called the QueryCache. Some of the great things about the QueryCache are:

  • its accessible from anywhere in your React application
  • its managed by react query, so you don't need to do any setup or maintenance for it
  • you're able to easily inspect the data
  • you're able to easily edit and query the cache from any other component in your app and use the data

Where Do I Move My Client State?

If you were previously mixing client and server state within one state store like Redux, you can leave your client state in Redux OR you can simply opt to use React's built in Context API to store your UI state; this will dramatically simply your application, tests and app maintenance

Should You Use React Query?

If you're mixing client and server state in your app with something like Redux etc, I highly recommend you checkout React Query.

My Experience

  • I've used React Query for nearly a whole year in both a web and React Native codebase 🎉. I'm happy to report that my team hasnt ever needed reach for Redux, and we really don't need to since React Context is managing our UI state, and all server state is delegating to React Query.

Other People's Experience

  • "If I really could go back in time and meet myself, in addition to the one thing I said, I would hand myself a flash drive with a copy of React Query." - Kent C Dodds
  • Removing 10,000 lines of data fetching code by using React Query instead of Redux - Tweet

References and Resources