DEV Community

Cover image for Why should you use react query for your react app - part 2
Benedict Steven for ClickPesa

Posted on

Why should you use react query for your react app - part 2

...For the introduction to react-query and useQuery hooks, Read my previous post Part one

In my previous post we explored the introduction to react query and useQuery hook.

In this post we explore useMutation hook in data creation, edition and deletion.

We will use Free API for creating user, editing user info, viewing and deleting user. In order to use the free api above, you will need to login to get your access token for authorization.

Lets create a form that to create user info.
The required user info are name, gender, email and status.

const App = () => { const handleSubmit = (e) => { e.preventDefault(); // submit user data const dataToSubmit = { name: e.target[0].value, email: e.target[1].value, gender: e.target[2].value, status: e.target[3].value, }; }; return ( <div className="App"> <form onSubmit={handleSubmit}> <label> Name: <input type="text" name="name" required placeholder="Name" /> </label> <br /> <label> Email: <input type="email" name="email" required placeholder="Email" /> </label> <br /> <label> Gender: <select name="gender" defaultValue="" required placeholder="Select gender" > <option disabled value=""> -- select an option -- </option> <option value="male">Male</option> <option value="female">Female</option> </select> </label> <br /> <label> Status: <select name="status" defaultValue="" required placeholder="Select status" > <option disabled value=""> -- select an option -- </option> <option value="active">Active</option> <option value="inactive">Inactive</option> </select> </label> <br /> <button>Create user</button> </form> </div> ); }; export default App; 
Enter fullscreen mode Exit fullscreen mode

Now that we can collect info, lets create our first user,
import useMutation from react-query

useMutation provides data and methods/callbacks to run and get data.

import { useMutation } from "react-query" const App = () => { const { // responses and functions to call mutation data, error, isError, isIdle, isLoading, isPaused, isSuccess, mutate, mutateAsync, reset, status, } = useMutation( // the function to call an api (accepts variables) mutationFn, // options and methods/callbacks { mutationKey, onError, onMutate, onSettled, onSuccess, retry, retryDelay, useErrorBoundary, meta, } ) // this will run synchronous mutate(variables, { onError, onSettled, onSuccess, }) // this will return a promise mutateAsync(variables, { onError, onSettled, onSuccess, }) } 
Enter fullscreen mode Exit fullscreen mode

Learn More

From the sample above, lets create our first user.

import axios from "axios"; import { useMutation } from "react-query"; const App = () => { const { data, error, isLoading, mutate } = useMutation( async (dataToSubmit) => { // call axios request const { data } = await axios.post( "https://gorest.co.in/public/v2/users?access-token=89945d7d4cea090a8135a824fa3100f5a057ee2c4bdf9c3b8ab01f7a20b16784", dataToSubmit, { headers: { "Content-Type": "application/json", }, } ); return data; } ); const handleSubmit = (e) => { e.preventDefault(); // submit user data const dataToSubmit = { name: e.target[0].value, email: e.target[1].value, gender: e.target[2].value, status: e.target[3].value, }; mutate(dataToSubmit); }; console.log(data, error); return ( <div className="App"> <form onSubmit={handleSubmit}> <label> Name: <input type="text" name="name" required disabled={isLoading} placeholder="Name" /> </label> <br /> <label> Email: <input type="email" name="email" required disabled={isLoading} placeholder="Email" /> </label> <br /> <label> Gender: <select name="gender" defaultValue="" required disabled={isLoading} placeholder="Select gender" > <option disabled value=""> -- select an option -- </option> <option value="male">Male</option> <option value="female">Female</option> </select> </label> <br /> <label> Status: <select name="status" defaultValue="" required disabled={isLoading} placeholder="Select status" > <option disabled value=""> -- select an option -- </option> <option value="active">Active</option> <option value="inactive">Inactive</option> </select> </label> <br /> <button disabled={isLoading}> {isLoading ? "Creating user" : "Create user"} </button> </form> </div> ); }; export default App; 
Enter fullscreen mode Exit fullscreen mode

Run the code above to create your first user. Congratulations on creating your first user 🎉.

Lets create a formal way to represent the flow, from creating a new user to viewing all users to viewing single user to editing single user and finally deleting single user. We will have mainly three screens,
First the screen for viewing all users - it will include a button to create new user and a list of all users.
Second is a screen we just created, for creating a single user ( we can make it available for editing user as well. )
Third is a screen for viewing single user with buttons to edit and delete user.

First install react-router-dom for easy routing. run

npm install react-router-dom 
Enter fullscreen mode Exit fullscreen mode

Then wrap your application in Browser router.
in main.jsx or index.jsx
with vite it will look like this

import React from "react"; import ReactDOM from "react-dom/client"; import { QueryClient, QueryClientProvider } from "react-query"; import { BrowserRouter as Router } from "react-router-dom"; import App from "./App"; const queryClient = new QueryClient(); ReactDOM.createRoot(document.getElementById("root")).render( <React.StrictMode> <QueryClientProvider client={queryClient}> <Router> <App /> </Router> </QueryClientProvider> </React.StrictMode> ); 
Enter fullscreen mode Exit fullscreen mode

Lets create our pages

users.jsx page

import axios from "axios"; import React from "react"; import { useQuery } from "react-query"; import { Link } from "react-router-dom"; const Users = () => { const { data, isLoading, error, refetch } = useQuery({ queryKey: ["FETCH_USERS"], queryFn: async () => { const { data } = await axios.get("https://gorest.co.in/public/v2/users", { headers: { Authorization: "Bearer 89945d7d4cea090a8135a824fa3100f5a057ee2c4bdf9c3b8ab01f7a20b16784", }, }); return data; }, }); if (isLoading) { return <p>Please Wait...</p>; } if (error) { return ( <div> <p>{error?.message}</p> <button onClick={() => refetch()}>Retry</button> </div> ); } return ( <div> <div style={{ marginBottom: "1rem", }} > <Link to={"/mutate-user"}>Create New User</Link> </div> <ul> {data?.map((user) => ( <li key={user?.id}> <Link to={`/user/${user?.id}`}>{user?.name}</Link> </li> ))} </ul> </div> ); }; export default Users; 
Enter fullscreen mode Exit fullscreen mode

user.jsx

import axios from "axios"; import React from "react"; import { useMutation, useQuery } from "react-query"; import { Link, useNavigate, useParams } from "react-router-dom"; const User = () => { // navigation hook const navigate = useNavigate(); // get url params const params = useParams(); const id = params?.id; const { data, isLoading, error, refetch } = useQuery({ queryKey: ["FETCH_USER", id], queryFn: async () => { const { data } = await axios.get( `https://gorest.co.in/public/v2/users/${id}`, { headers: { Authorization: "Bearer 89945d7d4cea090a8135a824fa3100f5a057ee2c4bdf9c3b8ab01f7a20b16784", }, } ); return data; }, }); // delete mutation const { error: mutateError, isLoading: mutateLoading, mutateAsync, } = useMutation(async () => { // call axios request const { data } = await axios.delete( `https://gorest.co.in/public/v2/users/${id}?access-token=89945d7d4cea090a8135a824fa3100f5a057ee2c4bdf9c3b8ab01f7a20b16784`, { headers: { "Content-Type": "application/json", }, } ); return data; }); if (isLoading) { return <p>Please Wait...</p>; } if (error) { return ( <div> <p>{error?.message}</p> <button onClick={() => refetch()}>Retry</button> </div> ); } return ( <div> <div style={{ display: "flex", gap: "1rem", marginBottom: "1rem", }} > <Link to={`/mutate-user?id=${id}`}>Edit User</Link> <button onClick={async () => { await mutateAsync(); // if success delete redirect to all users page navigate("/"); }} > {mutateLoading ? "Deleting user" : "Delete User"} </button> </div> {mutateError && ( <p style={{ color: "red", }} > {mutateError?.message} </p> )} <div> <p>Name: {data?.name}</p> <p>Email: {data?.email}</p> <p>Gender: {data?.gender}</p> <p>Status: {data?.status}</p> </div> </div> ); }; export default User; 
Enter fullscreen mode Exit fullscreen mode

mutate-user.jsx

import axios from "axios"; import { useEffect } from "react"; import { useMutation, useQuery } from "react-query"; import { useNavigate, useSearchParams } from "react-router-dom"; const MutateUser = () => { // navigate hook const navigate = useNavigate(); // find ID if present const [searchParams] = useSearchParams(); const id = searchParams.get("id"); // get user if there is an ID const { data: userData, isLoading: userLoading, error: userError, refetch, } = useQuery({ queryKey: ["FETCH_USER", id], queryFn: async () => { const { data } = await axios.get( `https://gorest.co.in/public/v2/users/${id}?access-token=89945d7d4cea090a8135a824fa3100f5a057ee2c4bdf9c3b8ab01f7a20b16784`, { headers: { Authorization: "Bearer 89945d7d4cea090a8135a824fa3100f5a057ee2c4bdf9c3b8ab01f7a20b16784", }, } ); return data; }, enabled: !!id, }); const { data, error, isLoading, mutateAsync } = useMutation( async (dataToSubmit) => { // call axios request // call post if its user creation and put if its user editing const { data } = await axios[id ? "put" : "post"]( `https://gorest.co.in/public/v2/users${ // pass id if editing user id ? "/" + id : "" }?access-token=89945d7d4cea090a8135a824fa3100f5a057ee2c4bdf9c3b8ab01f7a20b16784`, dataToSubmit, { headers: { "Content-Type": "application/json", }, } ); return data; } ); // navigate to user page when request was successfully useEffect(() => { if (data) { // redirect to user page navigate(`/user/${data?.id}`); } }, [data]); if (userLoading) { return <p>Please Wait...</p>; } if (userError) { return ( <div> <p>{userError?.message}</p> <button onClick={() => refetch()}>Retry</button> </div> ); } const handleSubmit = async (e) => { e.preventDefault(); // submit user data const dataToSubmit = { name: e.target[0].value, email: e.target[1].value, gender: e.target[2].value, status: e.target[3].value, }; await mutateAsync(dataToSubmit); }; return ( <div className="App"> <form onSubmit={handleSubmit}> <label> Name: <input type="text" name="name" // prefill data if exists defaultValue={userData?.name ?? ""} required disabled={isLoading} placeholder="Name" /> </label> <br /> <label> Email: <input type="email" name="email" // prefill data if exists defaultValue={userData?.email ?? ""} required disabled={isLoading} placeholder="Email" /> </label> <br /> <label> Gender: <select name="gender" // prefill data if exists defaultValue={userData?.gender ?? ""} required disabled={isLoading} placeholder="Select gender" > <option disabled value=""> -- select an option -- </option> <option value="male">Male</option> <option value="female">Female</option> </select> </label> <br /> <label> Status: <select name="status" // prefill data if exists defaultValue={userData?.status ?? ""} required disabled={isLoading} placeholder="Select status" > <option disabled value=""> -- select an option -- </option> <option value="active">Active</option> <option value="inactive">Inactive</option> </select> </label> <br /> {error && ( <p style={{ color: "red", }} > {error?.message} </p> )} <br /> <button disabled={isLoading}> {!id && <>{isLoading ? "Creating user..." : "Create user"}</>} {id && <>{isLoading ? "Editing user..." : "Edit user"}</>} </button> </form> </div> ); }; export default MutateUser; 
Enter fullscreen mode Exit fullscreen mode

Then lets setup our routing system

in the App.jsx

import React from "react"; import { Route, Routes } from "react-router-dom"; import MutateUser from "./pages/mutate-user"; import User from "./pages/user"; import Users from "./pages/users"; const App = () => { return ( <div> <Routes> <Route element={<Users />} path="/" /> <Route element={<User />} path="/user/:id" /> <Route element={<MutateUser />} path="/mutate-user" /> </Routes> </div> ); }; export default App; 
Enter fullscreen mode Exit fullscreen mode

Congratulations 🎉, we have created CRUD application with react-query Without managing any state ourselves Phew.

If you missed the introduction part and useQuery part, read my previous post

If you like this article there are more like this in our blogs, follow us on dev.to/clickpesa, medium.com/clickpesa-engineering-blog and clickpesa.hashnode.dev

Happy Hacking!!

Top comments (0)