DEV Community

Cover image for Managing state with React Query. 〽️
Franklin Martinez
Franklin Martinez

Posted on

 

Managing state with React Query. 〽️

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.

📌 Conclusion.

📌 Demonstration.

📌 Source code.

 

📢 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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

Then we install the dependencies.

npm install
Enter fullscreen mode Exit fullscreen mode

Then we open the project in a code editor (in my case VS code).

code .
Enter fullscreen mode Exit fullscreen mode

📢 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
Enter fullscreen mode Exit fullscreen mode

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>
        </>
    )
}
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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.

  1. 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>
        </>
    )
}
Enter fullscreen mode Exit fullscreen mode
  1. 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>
    )
}
Enter fullscreen mode Exit fullscreen mode
  1. 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>
        </>
    )
}
Enter fullscreen mode Exit fullscreen mode

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 = () => (