SaaS apps constantly contact the backend APIs to create, read, update, and delete data - CRUD. Using libraries like Redux to do state management with their complex boilerplate of Actions, Reducers, and Middlewares will get complicated as the size of the application grows. Redux Query keeps it simple.
I'm using React Query in my SaaS app, engyne.ai to fetch a list of sites data. When a new site is created, a POST request is made. The list of sites is then re-fetched, and a toast is displayed when the request is successful.
Using React Query in SaaS app In this post, you will learn how to:
- Fetch data using React Query and GET request
- Create new data using React Query and POST request
- Update data using React Query and PATCH request
- Show Toast when an API request is successful
Install React Query
npm i @tanstack/react-query
Fetch data using React Query
Let's start nice and easy with a simple GET request.
We can get the list of sites owned by a user with a specified email
by sending out a GET request using Axios. React Query has a hook called useQuery
that takes in:
- An array of keys - Used to name the query's data. It should be unique and serializable. If a variable is present, it is also passed onto the query function.
- A function to call - The Axios request that will run when the query is called.
import axios from "axios";
import { useQuery, useQueryClient } from "@tanstack/react-query";
const fetchSites = async (email: string) => {
const response = await axios.get(`api/users/${email}`);
return response;
};
const useSites = (email: string) => {
return useQuery(["sites", email], () => fetchSites(email));
};
export { useSites };
We can now consume these hooks in a React component by importing useSites
and use it like so:
import { FunctionComponent } from "react";
import { useSites } from "../../hooks/api";
interface AppProps {}
const App: FunctionComponent<AppProps> = () => {
const { data, isLoading, isFetching } = useSites("sukh@email.com");
if (isLoading) {
return <p>Loading...</p>;
}
return <div>{JSON.stringify(data)}</div>;
};
export default App;
Notice that you get the status of the query for free with React Query. You can use this to show Loading states in the UI.
Now that we can fetch our data. Let's take a look at how we can create new data.
Create new data using React Query
When creating a new resource, you should be making a POST request with Axios with the body containing the data.
React Query has another hook specifically for mutations called useMutation
. It takes just the function to call when the hook is used. This would be your Axios request.
When the new data is created, your previously fetched data will be stale. This is where useMutation
lets you specify a onSuccess
function that runs right after the query is successful. Using queryClient
the key you specified in useSites
hook, the data will automatically be re-fetched.
import axios from "axios";
import { useMutation, useQueryClient } from "@tanstack/react-query";
const useCreateSite = () => {
const queryClient = useQueryClient();
return useMutation((data: any) => axios.post("/api/sites", data), {
onSuccess: () => {
queryClient.invalidateQueries(["sites"]);
// good place to throw a toast/notification to let the user know
},
});
};
export { useCreateSite };
In your React component, you can trigger a mutation like so:
import { FunctionComponent } from "react";
import { useSites } from "../../hooks/api";
interface AppProps {}
const App: FunctionComponent<AppProps> = () => {
const { mutate: createSiteMutation } = useCreateSite();
return (
<div>
<button
onClick={() => {
createSiteMutation({
data,
});
}}
></button>
</div>
);
};
export default App;
Update data using React Query
Updating data is largely the same as Creating in the last section. You can useMutation
to specify a PATCH
Axios request and pass in the data to update. Once successful, invalidate the query using the query key you defined in the useSites
.
const useUpdateSite = () => {
const queryClient = useQueryClient();
return useMutation(
({ subdomain, ...data }: any) =>
axios.patch(`/api/sites/${subdomain}`, data),
{
onSuccess: () => {
queryClient.invalidateQueries(["sites"]);
},
}
);
};
export { useUpdateSite };
To use in React component:
import { FunctionComponent } from "react";
import { useUpdateSite } from "../../hooks/api";
interface AppProps {}
const App: FunctionComponent<AppProps> = () => {
const { mutate: updateSiteMutation } = useUpdateSite();
return (
<div>
<button
onClick={() => {
useUpdateSite({
data,
subdomain,
});
}}
></button>
</div>
);
};
export default App;
Bonus: Show a Toast when the API request is successful
When a user creates new data, it's good UX practice to show a notification on success. We can do this very quickly with React Query. I'm using the Toast component from Chakra UI but you can use another library like React Toastify as well.
import axios from "axios";
import { useMutation, useQueryClient } from "@tanstack/react-query";
import { useToast } from "@chakra-ui/toast";
const useCreateSite = () => {
const queryClient = useQueryClient();
const toast = useToast();
return useMutation((data: any) => axios.post("/api/sites", data), {
onSuccess: () => {
queryClient.invalidateQueries(["sites"]);
toast({
title: "Created site.",
description: "We've created a new site for you.",
status: "success",
duration: 9000,
isClosable: true,
});
},
});
};
export { useCreateSite };