Hello fellow developers!
Since the appearance of new features in React, such as "Hooks" and "Context,” we began to find Redux substitutes when we managed our app’s state. I made a small example of using React Query for this purpose in this opportunity, and I will talk about some great features it has.
Welcome to movies-react-query 👋
An example of react app using react-query and tailwind CSS
Prerequisites
- node >=14.15.4 <15.0
- npm >=7.0.0
Install
npm install
Usage
npm run start
Run tests
npm run test
Author
👤 Martin Mato
- Website: https://www.linkedin.com/in/martinmato/
- Twitter: @otamnitram
- Github: @otamnitram
- LinkedIn: @martinmato
Show your support
Give a ⭐️ if this project helped you!
To have real data, I used The movies database API to fetch data. You need to register yourself and ask for an API token that you can use in my repo.
How to use React Query?
Fetching data.
If you want to fetch data to be used in the app, it can be done very easily.
React Query provides us the useQuery
hook to fetch and control de state of the retrieved data.
Javascript
import { useQuery } from 'react-query'
function App() {
const { isLoading, isError, data, error } = useQuery('movies', fetchMovies)
}
In the example above, fetchMovies
is our async call that will return an array with all the movies; this can be an Axios call or a simple fetch. The useQuery
hook result contains some states we can use in our app.
isLoading
will be true when the query has no data yet, very useful to render a spinner while you can't show data to the user yet.isError
will be true if the async call returned an error, and, of course, theerror
state will give us more information about it. This is helpful if we need to render some error message when things go wrong.data
will bring us the result of the async call, and we can use it to render our data and show it to the user.
If you didn't notice, we never used anything but the useQuery
hook. We didn't use things such a useEffect
to pull the data to an useState
constant. React Query does all of that behind curtains for us.
If you need more info, check the documentation
Modifying data in the server.
Now imagine that you are already fetching and showing the movie data, and you want to add a new movie to the server and, of course, to the list.
To do so, first, you need to declare a new queryCLient
to have access to the cache. If you want to provide the same client through all the app, you maybe want to use QueryClientProvider
.
Once you have a new queryClient,
we can use the useMutation
hook to create a new movie.
javascript
function MoviesList() {
// Access the client
const queryClient = useQueryClient();
// Queries
const query = useQuery('movies', getMovies);
// Mutations
const mutation = useMutation(postMovie, {
onSuccess: () => {
queryClient.invalidateQueries('movies');
},
});
return (
<div>
<ul>
{query.data.map((movie) => (
<li key={movie.id}> {movie.title} </li>
))}
</ul>
<button
onClick={() => {
mutation.mutate({
id: Date.now(),
title: 'Terminator',
});
}}
>
Add Movie
</button>
</div>
);
}
There are some important things in the code above.
We declared our mutation very similar as we declare our queries. To use it, we call the mutate
function and pass the payload as parameters. Once the async call is done, we can use the onSuccess
option and invalidate the query we were using and refetch the data again by calling invalidateQueries
.
Similar to the useQuery
hook, the useMutation
hook also came with useful states in its response.
javascript
const { isLoading, isError, error, data, isSuccess } = useMutation(postMovie, {
onSuccess: () => {
queryClient.invalidateQueries('movies');
},
});
We can use isLoading
to indicate that something is being posted to the server. data
will give us the response of the async call. isError
and error
will give us information in case something wrong happens.
For more information, check the documentation
Pagination and "Load More"
In my example in the github repo I wanted to fetch all the movies, but the API forced me to fetch page by page. Thankfully, React Query also has a solution for this kind of case.
Very similar to when we use the useQuery
hook, we can use the useInfiniteQuery
, which has more useful state properties and a different and more suitable way to handle the data.
const {
data,
error,
fetchNextPage,
hasNextPage,
isFetchingNextPage,
status,
} = useInfiniteQuery('movies', getPopularMovies, {
getNextPageParam: (lastPage) =>
lastPage.page < lastPage.total_pages ? lastPage.page + 1 : undefined,
});
The first difference with useQuery
is the structure of the retrieved data. data
contains data.pages
that is an array of all the pages retrieved where every page contains what you retrieved in the API. data.pageParams
has all the parameters used to fetch the pages.
My getPopularMovies
expect a parameter with the next page's number to be fetched behind the curtains. The first time useInfiniteQuery
runs it will fetch page = 1
then getNextPageParam
will calculate the next page if any.
We can use hasNextPage
to run again useInfinitQuery
and fetch the next page, isFetchingNextPage
indicates that the call is being called, and status
tell us if everything was ok or an error occurs.
Maybe it cannot be evident at first, just only seeing a piece of the code, so I invite you to go to my repo and take a better look.
Other cool stuff
I want to include in the future some other cool features React Query has.
Prefetching allows you to fetch the data before it's needed, ideal for the cases that you can anticipate what the user will need.
Optimistic Updates will do the obvious, but the cool thing is that you can rollback your update if anything went wrong.
Also, I recommend looking at how caching works.
Devtools
React Query has a great devtools feature that can be easily being installed. It will show you the current state of your app.
import { ReactQueryDevtools } from 'react-query/devtools'
function App() {
return (
<QueryClientProvider client={queryClient}>
{/* The rest of your application */}
<ReactQueryDevtools initialIsOpen={false} />
</QueryClientProvider>
)
}
Final Thoughts
React Query is a powerful tool but doesn't completely replace other global state managers such as Redux or Mobx. There are some cases where React Query can work very well with other client-state libraries.
I hope this gave you an overlook of this awesome tool. Feel free to leave a comment with suggestions, questions, or if you think some changes have to be done in the repo.
Welcome to movies-react-query 👋
An example of react app using react-query and tailwind CSS
Prerequisites
- node >=14.15.4 <15.0
- npm >=7.0.0
Install
npm install
Usage
npm run start
Run tests
npm run test
Author
👤 Martin Mato
- Website: https://www.linkedin.com/in/martinmato/
- Twitter: @otamnitram
- Github: @otamnitram
- LinkedIn: @martinmato
Show your support
Give a ⭐️ if this project helped you!
Thank you for reading!
Top comments (7)
I am using this for my new project too which is an improvement on user experience.
Thanks for sharing this useful example @otamnitram
It was a new finding for me...thanks Martin!!
But some links in the article are broken please fix them.
Sure, thanks for noticing. I will take care of that ASAP
can we use react query on class component also ??
Very nice article!
I just used SWR, I'm gonna give React Query a try
Thanks for sharing :)
Thank you for reading!