Why should you use react query for your react app - part 2
Tags: webdev, react, reactquery, statemanagement
Published: 2023-03-03 08:33:42

...For the introduction to react-query and useQuery hooks,  Read my previous post [Part one](https://dev.to/clickpesa/why-should-you-use-react-query-for-your-react-app-2kfh)

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](https://gorest.co.in) 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.

```JSX
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;
```

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.
```JSX
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,
 })
}

```
[Learn More](https://react-query-v3.tanstack.com/reference/useMutation)
 
From the sample above, lets create our first user.
```JSX
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;

```

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
```BASH
npm install react-router-dom
```

Then wrap your application in Browser router.
in main.jsx or index.jsx
with `vite` it will look like this
```JSX
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>
);

```

Lets create our pages

users.jsx page
```JSX
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;

```

user.jsx
```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;

```

mutate-user.jsx
```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;

```

Then lets setup our routing system 

in the App.jsx
```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;

```

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](https://dev.to/clickpesa/why-should-you-use-react-query-for-your-react-app-2kfh)

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

Happy Hacking!!