React Query is a large and complete library that facilitates the work when making client-side requests to the server and even performs much more than that.
But Did you know that you can use this library as a state manager?, possibly an alternative to redux-toolkit, zustand, among others. In this article I will show you how to implement it this way.
🚨 Note: to understand this article you should have basic knowledge of how to use React Query and also some basic knowledge with TypeScript.
Table of contents.
📌 Technologies to be used.
📌 Creating the project.
📌 First steps.
📌 Creating the pages.
📌 Configuring React Query.
📌 Using React Query as status manager.📌 Creating the functions to make the requests.
📌 Getting the data with React Query.
📌 Adding new data to our state.
📌 Removing data from the state.
📌 Updating the status data.
📢 Technologies to be used.
- React JS 18.2.0
- React Query 4.20.4
- React Router Dom 6.6.1
- TypeScript 4.9.3
- Vite JS 4.0.0
- CSS vanilla (You can find the styles in the repository at the end of this post)
📢 Creating the project.
We will name the project: state-management-rq (optional, you can name it whatever you like).
npm create vite@latest
We create the project with Vite JS and select React with TypeScript.
Then we run the following command to navigate to the directory just created.
cd state-management-rq
Then we install the dependencies.
npm install
Then we open the project in a code editor (in my case VS code).
code .
📢 First steps.
First we are going to install a dom router to be able to create a couple of pages in our app.
npm install react-router-dom
So, let's create a src/layout folder to create a very simple navigation menu that will be on all the pages.
Inside src/layout we create the index.tsx file and add the following:
import { NavLink, Outlet } from 'react-router-dom'
type LinkActive = { isActive: boolean }
const isActiveLink = ({ isActive }: LinkActive) => `link ${isActive ? 'active' : ''}`
export const Layout = () => {
return (
<>
<nav>
<NavLink className={isActiveLink} to="/">Home 🏠</NavLink>
<NavLink className={isActiveLink} to="/create">Create ✍️</NavLink>
</nav>
<hr className='divider' />
<div className='container'>
<Outlet />
</div>
</>
)
}
Then in the src/App.tsx file we are going to delete everything. And we are going to create our basic routes.
Note: We are going to set the routes using createBrowserRouter, but if you want you can use the components that react-router-dom still has like
<BrowserRouter/>,<Routes/>,<Route/>, etc. instead of createBrowserRouter.
By createBrowserRouter we are going to create an object where we will add our routes. Note that I only have a parent route, and what I show is the navigation menu, and this route has 3 daughter routes, which for the moment have not been created their pages.
Finally we create the component App that we export by default, this component is going to render a component of react-router-dom that is the <RouterProvider/> that receives the router that we have just created.
And with this we can navigate between the different routes.
import { createBrowserRouter, RouterProvider } from 'react-router-dom';
import { Layout } from './layout';
import { Home } from './pages/home';
const router = createBrowserRouter([
{
path: "/",
element: <Layout />,
children: [
{
index: true,
element: <>create</>,
},
{
path: "/create",
element: <>create</>,
},
{
path: "/:id",
element: <>edit</>,
},
]
}
]);
const App = () => ( <RouterProvider router={router} /> )
export default App
Then we will come back to this file to add more stuff 👀.
📢 Creating the pages.
Now we are going to create the three pages for the paths we defined earlier.
Create a new folder src/pages and inside create 3 files.
- home.tsx
In this file we are only going to list the data that will come from the API, so for the moment we will only put the following:
import { Link } from 'react-router-dom'
export const Home = () => {
return (
<>
<h1>Home</h1>
<div className="grid">
<Link to={`/1`} className='user'>
<span>username</span>
</Link>
</div>
</>
)
}
- createUser.tsx.
This page is only for creating new users or new data. So we will create a form. In this occasion I am not going to use a state to control the input of the form, but simply I will use the event that emits the form when it executes the onSubmit of the same one (It is important to put the attribute name to the input).
export const CreateUser = () => {
const handleSubmit = async (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault()
const form = e.target as HTMLFormElement
const data = Object.fromEntries(new FormData(form))
// TODO: create new user
form.reset()
}
return (
<div>
<h1>Create User</h1>
<form onSubmit={handleSubmit} className='mt'>
<input name='user' type="text" placeholder='Add new user' />
<button>Add User</button>
</form>
</div>
)
}
- editUser.tsx
In this page the selected user will be edited, we will obtain his ID by means of the parameters of the URL, as we established it when we created the router.
import { useParams } from 'react-router-dom';
export const EditUser = () => {
const params = useParams()
const { id } = params
if (!id) return null
return (
<>
<span>Edit user {id}</span>
</>
)
}
Now we need to place these pages in the router!
import { createBrowserRouter, RouterProvider } from 'react-router-dom';
import { Layout } from './layout';
import { CreateUser } from './pages/createUser';
import { EditUser } from './pages/editUser';
import { Home } from './pages/home';
const router = createBrowserRouter([
{
path: "/",
element: <Layout />,
children: [
{
index: true,
element: <Home />,
},
{
path: "/create",
element: <CreateUser />,
},
{
path: "/:id",
element: <EditUser />,
},
]
}
]);
const App = () => (