dev-resources.site
for different kinds of informations.
Speeding up React Development with TanStack Query
I just finished implementing TanStack Query (formerly known as React Query) in a project that Iāve been working on for the past month and a half (more on that soon!), and itās one of the more elegant libraries that Iāve worked with.
Itās very simple, allows for easy synchronization between your frontend and backend, and also great at reducing server load with front-end caching. Letās go into a bit more detail.
So what even is TanStack Query? š¤
TanStack Query (TSQ) is responsible for managing responsibilities like data fetching, caching, synchronization, and managing server state (you can read more about this on the documentation ā Iād prefer to stay DRY). Itās very possible to build a React frontend without it, but Iāve found that itās incredibly helpful, and it abstracts away a lot of the repetitive parts of React.
Allow me to give you an example. Hereās what my code looked like before I integrated TSQ:
function Feed() {
const [posts, setPosts] = useState();
const [page, setPage] = useState(1);
const [isLoading, setIsLoading] = useState(true);
useEffect(() => {
getFeed(1).then((res) => {
setPosts(res);
setIsLoading(false);
});
}, []);
function formSubmitHelper(e) {
e.preventDefault();
setIsLoading(true);
getFeed(page).then((res) => {
setPosts(res);
setIsLoading(false);
});
}
if (isLoading) {
return <CircularProgress />;
}
if (!posts || posts.length === 0) {
return <h1>There were no posts to be shown!</h1>;
}
return (
<div id="feed">
{posts.map((content, index) => (
<BlogPostThumbnail />
))}
<Paginator />
</div>
);
}
Now, if you havenāt used TSQ before, this probably looks pretty good. Thereās nothing wrong with the code so to speak; after all, it functions appropriately and passes the unit and integration tests that I wrote for it (more on that in a future post!).
However, this code isn't very extendable or reusable. If I wanted to check for and render an error message, I would have to add in something along the lines of this:
const [error, setError] = useState(āā)
// ā¦
function formSubmitHelper(e) {
e.preventDefault();
setIsLoading(true);
getFeed(page).then((res) => {
if(res.ok) {
setPosts(res);
setIsLoading(false);
}
else {
setError(res.error);
}
});
// ā¦
if (error) {
<h1>{error}</h1>
}
}
Granted, this is a bit of a rough draft. The actual implementation may take up more ācode spaceā, or it may take up less. Nevertheless, itās pretty clear that thereās a lot of boilerplate code that is needed just to render a simple error message. This can cause bloat in React applications (in my case, a SPA), as well as code that is exceedingly hard to maintain, debug, and test.
A practical application šø
This is where TSQ comes in. Instead of adding another useState()
hook, we can use TSQās useQuery()
hook to instead replace almost every useState()
hook. Hereās an example based off of the previous bit of code:
function Feed() {
const [page, setPage] = useState(1);
const { data, isLoading } = useQuery({
queryKey: ["getFeed", page],
queryFn: () => getFeed(page),
});
function formSubmitHelper(e) {
e.preventDefault();
setPage(e.target[0].value);
}
if (isLoading) {
return (
<div id="feed">
<CircularProgress />
</div>
);
}
if (!data || data.length === 0) {
return (
<div id="feed">
<h1>There were no posts to be shown!</h1>
<Paginator />
</div>
);
}
return (
<div id="feed">
{data.map((content, index) => (
<BlogPostThumbnail />
))}
<Paginator />
</div>
);
}
Instead of adding another useState()
hook for every single āthingyā we want to keep track of, we can simply take what we want from useQuery()
. If we need to check for an error, we can simply change const { data, isLoading } ā¦
to const { data, isLoading, isError } ā¦
. Pretty cool, huh?
Some other advantages š
Additionally, TSQ handles the function call all on its own. Notice that in the above code snippet, thereās no more useEffect()
. We donāt need it anymore, because the function call is completely handled by TSQ. Additionally, each time any of the state involved in the queryFn
changes (in this case, when setPage()
is called), the query is automatically called again.
"But what about the caching?", you might ask. Iāll elaborate on that right now. TSQ can potentially reduce server load by implementing frontend caching, and while it may sound confusing, itās actually pretty simple. Hereās a little diagram:
As you might be able to tell, the cache is basically just a hashmap, where queryKey is the key. This is super useful forā¦ really anything! Since ācachingā doesnāt take any extra leg work to set up, TSQ can give you a little performance boost right out of the box!
Wrapping it up š
TSQ is a wonderful library, but one of the things that makes it so wonderful is that itās incredibly simple, and consequently, thereās not much to write about.
So on that note, thanks for reading! If you have any questions, feel free to ask. However, the documentation might prove a better resource, as Iām still very new to this library.
P.S: At the time of writing this, Iām working on a whole bunch of unit and integration tests with Vitest and Reacting Testing Library, so if youād like to see an article about that, please do leave a like or follow me so you can be notified when I publish said article!
Featured ones: