Table of Contents
In this article about react-query library we are going to talk about one of those libraries that have grown a lot over the last year, and rightly so. In the React universe, the react-redux stack is well known, it has been the standard for a long time until developers (including those of redux), we have seen how we have used redux beyond our possibilities. We have tried to apply redux in every application ever without even asking ourselves if it was useful or not. We just knew it, we knew how it worked and well, it was our comfort zone.
That has not scaled as well as we expected, redux complicated many of the actions we wanted to do in our application, forcing us to create/modify infinite layers to add a single action in the UI. This has been changing over time, and thanks to libraries like react-query library, we have seen how redux was not the only solution, in fact, in many occasions, it was not even a solution.
What is react-query library?
react-query library is a react library (nobody expected it, huh?) that based on hooks, will allow you to make calls to your API and will manage the cache for you. As easy as it sounds (example taken from the official documentation):
function Example() {
const { isLoading, error, data } = useQuery('repoData', () =>
fetch('https://api.github.com/repos/tannerlinsley/react-query').then(res =>
res.json()
)
)
if (isLoading) return 'Loading...'
if (error) return 'An error has occurred: ' + error.message
return (
{data.name}
{data.description}
? {data.subscribers_count}{' '}
✨ {data.stargazers_count}{' '}
? {data.forks_count}
)
}
As you can see, the hook receives two mandatory parameters: the key and an asynchronous function. The key must be unique for each call, since it will be used to cache the result of the function. The function, as expected, will be in charge of making the HTTP call. Here we should not be tempted to avoid creating our service layer that will take care of this call and make the necessary mappings.
In this article we are not going to go into how to use react-query library since its documentation is very complete and very well explained, in addition, they have a very active discord server where they can solve doubts. (https://react-query.tanstack.com/overview)
Practical example
In our example we are going to list some users from a backend and we have to be able to filter that list by state or city. To do this, we will start with our useQuery hook:
export const useFetchUsers = (filters: FilterMap) => {
return useQuery(
["users", filters],
() => fetchUsers(filters)
)
}
TIP: Do not use directly useQuery in your container, better to use it inside a custom hook so you can reuse it, among other things.
As we can see, the key is a list (https://react-query.tanstack.com/guides/query-keys), besides, one of the elements in the list is a variable. This will cause the function to be executed again when filters change.
In our container we will have to call useFetchUsers passing it our values to filter that list (if any). To do this, we will use a hook made for the occasion in which the values will persist in the URL and will recover them from there and then pass them to our API.
const filters = useFilters(["city", "status"]);
const users = useFetchUsers(filters.current);
useFilters hook is not complicated to understand, at least its interface (https://github.com/OscarGalindo/react-query-filtering-example/blob/631bcbf2cc8ceb56591a71136420c75de70f82d7/src/hooks/filters.hook.tsx). At the end is a hook that exposes one method and the current filters.
The example of reading the current filters (read from the following URL: https://frontend/users?city=East+Tomas&status=blocked) you have in the previous code, filters.current returns the current filter read from the URL.
To change the filters, the hook provides a single method that updates all filters (there is nothing to edit/delete a single filter, yet…), this is filters.apply(FilterMap).
In the example, we wanted to take the filters to a modal to complicate the UI a bit and see how it would work. At the end we play with the inheritance of the components, the UserContainer is the one that opens the modal so we pass the apply method to the Dialog:
const [showFilters, hideFilters] = useModal(() => , [filters.current])
This is where inside we have a form that when doing submit will call the onApply, as easy as that. The hook will internally update the URL and react-query will receive the new filters, running the new query with new filters.
As you can see, what in redux would surely be 5-6 files, about 5-10 actions with their associated sagas and reducers, using react-query along with the useFilters hook is less than 10 lines.
Conclusion
The article has been more technical than conceptual, but there is a lesson to be learned, and that is that we should not let ourselves be carried away by the current, we must think things through, and more so in the frontend world where it changes very frequently. This article is not based on destroying redux in favor of react-query library (or similar libraries), nor is it based on destroying the whole thing.
If we go back to the example, the state* is clearly in the server, we delegate to it the responsibility of everything, we only need that data to paint it, we are not talking about an application that can get to work offline or some special case.
The key here would be to understand where we want the state, should the frontend have that responsibility? Should it be shared? Should the backend be in charge of everything? Those are the questions we should ask ourselves in order to find the best tool.
*State: When we talk about state we are referring not only to the data, but to the behavior of the same.
- Live example: https://react-query-filtering-example.vercel.app/
- Repository: https://github.com/OscarGalindo/react-query-filtering-example
Author
-
Software developer with over 16 years experience working as Fullstack Developer & Backend Developer.
View all posts