Cover image by Lawrence Eagles in the article: What’s new in React Query 3
Hey everyone 👋
I'm back again. I am always grateful for the encouragement. Thank you for the 💖, for taking the time to read the lengthy Part Two: QueryClient configuration of this series. To my new followers; Thank you! I'll do my best not to disappoint you.
In part two, we talked about overriding some of the defaults that come with react-query
by setting our custom defaults for both queries
and mutations
. We set our custom defaults on such options like retry
, staleTime
, cacheTime
, refecthOnMount
and a couple of others for our queries
and retry
for mutations
.
Table of contents
Intro
In this part, we will be learning how to fetch data from an API using the useQuery
hook. I promised to show you how we can override the defaults we set earlier, so we will have a look at that too. Always remember to use the table of contents above to jump to sections relevant to you.
The useQuery hook
We start by installing axios and refactoring a bit.
npm i axios
Our QueryClient
goes to a new file ./src/util/queryClient.js
import { QueryClient} from 'react-query';
const queryClientConfig = {
defaultOptions: {
queries: {
retry: 2,
staleTime: 1000 * 30,// 30 seconds
cacheTime: 1000 * 30, //30 seconds
refetchOnMount: "always",
refetchOnWindowFocus: "always",
refetchOnReconnect: "always",
refetchInterval: 1000 * 30, //30 seconds
refetchIntervalInBackground: false,
suspense: false,
},
mutations: {
retry: 2,
},
},
export const queryClient = new QueryClient(queryClientConfig);
If you just got here, we explained this snippet here
We clean our App.js
thus
import { QueryClientProvider } from 'react-query';
import { ReactQueryDevtools } from 'react-query/devtools';
import { queryClient } from "./util/queryClient";
function App() {
return (
<QueryClientProvider client={queryClient}>
{/* The rest of your application */}
<ReactQueryDevtools initialIsOpen={false} />
</QueryClientProvider>
)
}
We will also create a queryKeys.js
file at ./src/util/queryKeys.js
.
This file will host all the query keys for our application.
export const fetchPostsKey = "FETCH_POSTS";
Create a fetchPosts.service.js
file at ./src/services/fetchPosts.service.js
and create your simple async function to fetch a list of posts.
We will be using the JSONPlaceholder REST API for this demo.
import axios from "axios";
/**
* @desc fetch a list of posts
*/
export const fetchPosts = async () => {
const res = await axios.get(`https://jsonplaceholder.typicode.com/posts`);
return res?.data;
};
Fetching data
Create a Posts.js
component at ./src/components/Posts.js
Remember to import your Posts.js
component to your App.js
...
function App() {
return (
<QueryClientProvider client={queryClient}>
<Posts/>
<ReactQueryDevtools initialIsOpen={false} />
</QueryClientProvider>
)
}
Posts.js
import { useQuery } from "react-query";
import { fetchPosts } from "../../services/fetchPosts.service";
import { fetchPostsKey } from "../../util/queryKeys";
const Posts = () => {
const { isLoading, isError, isSuccess, refetch, remove, data, error} = useQuery(fetchPostsKey, fetchPosts);
return (
<div>
{isLoading ? (
<div>Loading...</div>
) : isError ? (
<div>An error while fetching posts</div>
) : (
data?.map((post) => (
<div key={post?.id}>
<div>{post?.title}</div>
<div>{post?.body}</div>
</div>
))
)}
</div>
);
};
export default Posts;
The useQuery
hook accepts a query key as its first argument and the query function as its's second argument.
The query key is required, the query function also is required as there was not default query function defined for our queries
in QueryClient
. Let's take a quick brush at the items in the destructured object useQuery
returns to us.
isLoading
: It is a boolean
value that returns true
when the query has no data and is currently fetching and false
when not.
isError
: Also a boolean
value. It return true
when the query attempt results in an error.
isSuccess
: Returns true
if the query has received a response with no errors and is ready to display its data. isSuccess
is false
when query is not yet resolved or results in an error.
refetch
: This is a function that manually refetches the query.
remove
: This function is used to manually remove the query from cache.
data
: It is the response from the last successful query. data
will be undefined
if query fails for the first time.
error
: It is the error response from your query. It is defined when your query is in an isError
state.
The useQuery
hook returns more values in the destructured object than described here, but this few I chose for the scope of this article. You can read more about the useQuery
hook here.
Passing variable(s) to a query function
So, what if you want to pass a variable or variables to your query function? E.g. you have a function that fetches a single post and it requires you to pass in a post id
; What do you do?
Let's see how it is done.
We will a new key entry in a queryKeys.js
file at ./src/util/queryKeys.js
.
...
export const fetchSinglePostKey = "FETCH_SINGLE_POST";
Create also a fetchSinglePost.service.js
file at ./src/services/fetchSinglePost.service.js
and create your simple async function to fetch a single post by id
.
fetchSinglePost.service.js
import axios from "axios";
/**
* @desc fetches a single post
*/
export const fetchSinglePost = async ({queryKey}) => {
const [_key, id] = queryKey
const res = await axios.get(`https://jsonplaceholder.typicode.com/posts/${id}`);
return res?.data;
};
Post.js
import { useQuery } from "react-query";
import { fetchSinglePost } from "../../services/fetchSinglePost .service";
import { fetchSinglePostKey } from "../../util/queryKeys";
const Post = () => {
// fetching the post with the id of 1
const { isLoading, isError, isSuccess, refetch, remove, data, error} = useQuery([fetchSinglePostKey, 1], fetchSinglePost );
return (
<div>
{isLoading ? (
<div>Loading...</div>
) : isError ? (
<div>An error while fetching post</div>
) : (
<div >
<div>{data?.title}</div>
<div>{data?.body}</div>
</div>
)
)}
</div>
);
};
export default Post;
Here, we are no more using a string
value for our query key but an array
, passing in the query string first and the post id
as required by our query function fetchSinglePost.service.js
.
The fetchSinglePost
function declared in useQuery
hook is passed in a context, this context has queryKey
array
nested in it. This queryKey
array contains your query string as the first item in the array and your id
variable for fetching our single post.
Remember to import your Post.js
component to your App.js
...
function App() {
return (
<QueryClientProvider client={queryClient}>
...
<Post/>
<ReactQueryDevtools initialIsOpen={false} />
</QueryClientProvider>
)
}
Overriding query defaults
We have seen two demonstrations using useQuery
but don't forget that they all are operating within the queries
default we set earlier. To override some of the global configurations, we pass an object as a third argument to the useQuery
hook. Every option you declare a new value, that option is overridden for that useQuery
instance only.
...
const { isLoading, isError, isSuccess, refetch, remove, data, error} = useQuery([fetchSinglePostKey, 1], fetchSinglePost, {
refetchInterval : 3* 1000 //3 seconds
});
...
What this snippet above implies is that, although we configured react-query
globally to refetch queries every 30 seconds, This particular query will refetch every 3 seconds; breaking away from the global configurations.
Conclusion
The returned data from our queries are persisted in a cache. In the next part, we will discuss how the to interact with this cache.
Thank you all for your support. If you are beginner and haven't written something, do that today! Please give me a 💖 if this post or part of it has helped you. Comments are welcomed too.
Follow me on twitter @NnajioforEmma10
Top comments (2)
I have created an account just to say thank u mate
You are super welcome