The benefits of using React Query over Redux
What is React Query?
React Query is a highly configurable library for managing server-state in your application. It has its own views and philosophy on storing, leveraging, and updating server data without touching any “global state”. React Query handles caching, background updates, allows us to deal with outdated requests, mutations, and pagination out of the box with little-to-zero configuration. It’s created by Tanner Linsley, an open-source software engineer and maintainer of react table, react charts, react form, and many more. With React Query, we can greatly decrease the need for writing our data-fetching logic by hand - the API is straightforward and powerful. All you need are 2 hooks and 1 utility. There is much more to the API, but most of the use cases can be covered with those 2 hooks. It also works great with TypeScript.
React Query basics
Now let's dive into more specifics using examples. We needed a ready-to-go state manager for a small React application and did not want to spend much time writing tons of boilerplate (hello, Redux), we also wanted to learn something new, and this project was a great opportunity to do this, every project is!
So we decided to strip out our boilerplate to a minimum. We wanted something simple and efficient for managing server-state, and React Query was the answer. Let’s get to the code already.
First, we need to wrap our application with QueryProvider.
Congratulations, this is pretty much everything we need to set up. Now let’s move on to the next step - handling actual data requests.
The main things we want to be able to do are fetch, read, and update our data. We have useQuery and useMutation hooks for this.
For fetching and reading, it’s as simple as it looks. The first parameter you need to pass is a query key. It’s a key under which the data will be stored in the React Query cache. The second parameter is your fetch function, which has to return some data to be stored. And that’s it! No more reducers, action creators, middlewares. Focus on your app, not on getting data from the server. The hook returns loading flags as well as the fetched data and more.
You can optionally provide the 3rd argument to useQuery to set options if you need something more complex. You can set up a refetch timeout, make data refetch on window focus, call some function before the query starts loading, etc. Such a query will run automatically when your component mounts. Of course, you can change this behavior by providing an "enabled" flag to the options if you want to run this query conditionally.
Next up, we want to change some data, perform CRUD operations. useMutation to the rescue. Let’s say we want to create a todo.
As you can see, useMutation is a simple hook with two parameters:
- The function which handles your request.
- The options parameter where you can extend some of the logics such as success and error handlers and more.
The result of calling this hook is:
- mutate: action function to trigger the post call.
- isLoading: loading state of your request.
- error: this indicates the errors in case there is an error in the request.
This concludes the minimal basics, so you can try it yourself already! If you are eager to learn more about React Query, we have one more feature that we hope will push you to explore this library if you haven't already.
Developer experience is important. But what’s more important? Of course, user experience! Despite the fact that React Query is a library for managing server-state, it helps us to build a “responsive” UI with the help of the cool feature called optimistic updates. So let's move to this more advanced and handy feature.
When we perform a mutation, usually, we don’t need to wait for the server to respond. We know beforehand what data will look like, at least part of it that is needed for our UI to render. Imagine you are adding a new todo to the list. When you submit it, you show some loader while the server is working hard on adding your todo to the list. Eventually, it responds to you with the data, and you can show it to the user. This is not ideal for UX. Here comes the utility we were talking about before. Let’s continue improving our todo creation, we will get familiar with some useMutation options:
We pass an onMutate function, which will run every time before the mutation starts. First, we cancel all todos queries that might be fetching at this time. Then, using queryClient, we can derive the current state of the cache under previousTodos. Then we just add a new todo to the list. And remember, it already happened before we even fired our request to the server. So the user will instantly see that a new todo is added.
But what do we do to get the real data? After the response arrives, we know that our todo list is stale, and we need to fetch new data. This is simple, we just need to use the onSuccess property in useMutation options. We say: invalidate all queries under the “todos” key. It will refetch the list of todos and it will arrive with the new todo!
This is great, but what if something went wrong and the server responds with an error? How do we deal with this situation? We can just “roll back” our data to what it was before! To do this, the onMutate handler option allows you to return a value that will later be passed to the onError handler as the last argument.
These are just some use cases. If it is still too much code for you, you can write your own hooks to reuse your logic. For example:
This way you can reuse it without writing those verbose constructions every time.
Conclusion
While Redux has been a stalwart in managing state in React applications for years, React Query offers a refreshing alternative that simplifies server-state management without the overhead of maintaining a global state. With React Query, you can focus more on building your application logic rather than wrestling with reducers, action creators, and middleware. While Redux still has its place in certain scenarios, React Query's simplicity, powerful features, and seamless integration with TypeScript make it a compelling choice for modern React applications.
We've covered the basic React Query concepts, but there are many more features to discover! Such as infinite queries, pagination, auto-refetching, polling, and many more that React Query is packed with. The documentation is very extensive and clear. Definitely worth checking out.
Interested to hear more about our approach? Never hesitate to reach out at hello@panenco.com.
See also
Are you looking for an entrepreneurial digital partner? Reach out to hello@panenco.com or schedule a call