[ReactJS] Queries in React Query
Introduction
In this article, we are going to take a look at queries that you can use in react query.
Custom Query Hooks
Using react query, you can create your own custom query hooks and use it in different files. Remember that you cannot use hooks in conditional statements though. Custom query hoooks are useful for reducing redundant code and making your code more concise. All you have to do is add an id variable to the query key array.
1
2
3
4
5
6
7
8
const fetchDataById ({ queryKey }) => {
const dataId = queryKey[1]
return axios.get(`http://localhost:8080/api/data/${dataId}`)
}
export const useDataById = (dataId) => {
return useQuery(['data', dataId], fetchDataById)
}
Parallel Queries
Parallel queries are used when a component needs to call multiple apis to fetch data. It is simple as calling two useQueries. When you want to use the values offered be useQuery, you have to use aliases to reconstruct the values.
1
2
3
4
5
6
7
8
9
10
11
12
13
const fetchFriends () => {
return axios.get(`http://localhost:8080/api/friends`)
}
const fetchFamily () => {
return axios.get(`http://localhost:8080/api/family`)
}
export const ParallelQueries = () => {
const {data: Friends} = useQuery(['friends'], fetchFriends)
const {data: Family} = useQuery(['family'], fetchFamily)
return <div>parallelQueries<div>
}
Dynamic Parallel Queries
Dynamic parallel queries are used when you need to fetch multiple related or independent data sets concurrently. We use useQueries
instead of useQuery
when we want to perform dynamic parallel queries. The returned object from using useQueries
will be an array of the fetched data.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
const fetchFriends = (friendId) => {
return axios.get(`http://localhost:8080/api/friends/${friendId}`)
}
export const dynamicParallelQueries = (friendsIdList) => {
const result = useQueries(
friendsIdList.map(id => {
return {
queryKey: ['friends', id],
queryFn: () => fetchFriends(id),
}
})
)
return <div>dynamic parallel queries</div>
Dependent Queries
Dependent queries are used when you want queries to run after the other. One thing to remind is that for the queries that come after the first query, their queries will depend on the data fetched from the first query. Hence, we have use the enabled
options to not fetch data until we have fetched the data from the first query. Double negation turns a “truthy” or “falsy” value into a Boolean value, true
or false
.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
const fetchUserByEmail = (email) => {
return axios.get(`http://localhost:8080/api/user/${email}`
}
const fetchCourseById = (id) => {
return axios.get(`http://localhost:8080/api/course/${id}`
}
export dependentQueries = (email) => {
const {data: user} = useQuery(['user', email], () => fetchUserByEmail(email))
const id = user?.id // check if user is fetched or not
const {data: course} = useQuery(['course', id], () => fetchCourseById(id), { enabled: !!id})
return <div>dependent queries</div>
Initial Query Data
Initial query data is useful when you want to use data already present in the query cache from the previous query to render partial data to the user. We use useQueryClient
to use data already present in the cache using the query key.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
export const userCourseData = (courseId) => {
const queryClient = useQueryClient();
return useQuery(["course", courseId], fetchCourse, {
initialData: () => {
const course = queryClient
.getQueryData("course")
?.data?.find((course) => course.id === parseInt(courseId));
if (course) {
return { data: course };
} else {
return undefined;
}
},
});
};
Paginated Queries
Paginated queries are used to fetch data based on pages. One powerful option that you can use with paginated queries is keepPreviousData
. This option allows to keep the previous data when navigating to the next page.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
function Todos() {
const [page, setPage] = React.useState(0);
const fetchProjects = (page = 0) =>
fetch("/api/projects?page=" + page).then((res) => res.json());
const { isLoading, isError, error, data, isFetching, isPreviousData } =
useQuery({
queryKey: ["projects", page],
queryFn: () => fetchProjects(page),
keepPreviousData: true,
});
return (
<div>
{isLoading ? (
<div>Loading...</div>
) : isError ? (
<div>Error: {error.message}</div>
) : (
<div>
{data.projects.map((project) => (
<p key={project.id}>{project.name}</p>
))}
</div>
)}
<span>Current Page: {page + 1}</span>
<button
onClick={() => setPage((old) => Math.max(old - 1, 0))}
disabled={page === 0}
>
Previous Page
</button>{" "}
<button
onClick={() => {
if (!isPreviousData && data.hasMore) {
setPage((old) => old + 1);
}
}}
// Disable the Next Page button until we know a next page is available
disabled={isPreviousData || !data?.hasMore}
>
Next Page
</button>
{isFetching ? <span> Loading...</span> : null}{" "}
</div>
);
}
Infinite Queries
Infinite queries are used when you want to load more data. useInfiniteQuery
is used for infinite queries.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
import { useInfiniteQuery } from "@tanstack/react-query";
function Projects() {
const fetchProjects = async ({ pageParam = 0 }) => {
const res = await fetch("/api/projects?cursor=" + pageParam);
return res.json();
};
const {
data,
error,
fetchNextPage,
hasNextPage,
isFetching,
isFetchingNextPage,
status,
} = useInfiniteQuery({
queryKey: ["projects"],
queryFn: fetchProjects,
getNextPageParam: (lastPage, pages) => lastPage.nextCursor,
});
return status === "loading" ? (
<p>Loading...</p>
) : status === "error" ? (
<p>Error: {error.message}</p>
) : (
<>
{data.pages.map((group, i) => (
<React.Fragment key={i}>
{group.data.map((project) => (
<p key={project.id}>{project.name}</p>
))}
</React.Fragment>
))}
<div>
<button
onClick={() => fetchNextPage()}
disabled={!hasNextPage || isFetchingNextPage}
>
{isFetchingNextPage
? "Loading more..."
: hasNextPage
? "Load More"
: "Nothing more to load"}
</button>
</div>
<div>{isFetching && !isFetchingNextPage ? "Fetching..." : null}</div>
</>
);
}
Conclusion
There are different kinds of queries that you can use in react query. Since we have a better understanding now of what kind of queries react query have, we can use the ones that are needed in our future projects!