Build a CRUD App in React with Tanstack Query and Material UI [Part 1]
Tags: react, reactquery, tutorial, beginners
Published: 2024-01-07 23:53:36

*If simply reading this tutorial isn't enough for you, you [can go here and write code alongside](https://www.frontendly.dev/ihor-filippov/build-a-crud-app-in-react-with-tanstack-query-and-material-ui/1) it.*

---


In order to follow along with this tutorial, you'll need a basic knowledge of HTML, CSS, and JavaScript/ES6. You should also know the fundamentals of React, which you can learn by reading [Quick Start with React](https://react.dev/learn).

In this tutorial we will make a simple CRUD app. We will fetch users from the remote API and you will be able to add, update or delete them.

While implementing this app we will use:

* [React Material UI](https://mui.com/material-ui/)
* [TanStack Query](https://tanstack.com/query/latest/)
* custom hooks
* React Context API

Here is a diagram of what we will implement.


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/fypq5j3s34b3s8ut8v0k.png)

As you can see, here are two sections each with its own responsibility. One for create and edit user data and another for visualize the list of users.

First, let's a component responsible for all users data on the page.

`components/users.js`
```jsx
import { Container } from '@mui/material';

export const Users = () => {
  return (
    <Container 
      maxWidth="md" 
      sx={{ margin: '20px auto' }}>
    </Container>
  );
}
```
The `Container` component is used to center your content horizontally.

* `maxWidth="md"`: This sets the maximum width of the container. The `md` value means that the maximum width is set to the value defined for medium-sized screens in Material-UI's theme. This is a way to make the component responsive.
* `sx={{ margin: '20px auto' }}`: This is a shorthand for inline styles. `sx` is a special property provided by Material-UI for customizing styles.

Then, let's implement a component to render list of users.

`components/users-table.js`
```jsx
import { 
  Paper, 
  Table, 
  TableBody, 
  TableCell, 
  TableContainer, 
  TableHead, 
  TableRow 
} from "@mui/material"

export const UsersTable = () => {
  return (
    <TableContainer component={Paper}>
      <Table>
        <TableHead>
          <TableRow>
            <TableCell>Name</TableCell>
            <TableCell>Email</TableCell>
          </TableRow>
        </TableHead>
        <TableBody></TableBody>
      </Table>
    </TableContainer>
  );
}
```
Let's explain the code above.

* `<TableContainer component={Paper}>`: The `TableContainer` component is used as a container for the `Table`. It uses the `Paper` component as its base, which gives it a Material design look with a shadow and background.
* `Table`, `TableHead`, `TableBody`, `TableRow` and `TableCell` are just wrappers for native elements of the table.

Now, import `UsersTable` inside `Users`.

`components/users.js`
```jsx
import { Container } from '@mui/material';
import { UsersTable } from './users-table';

export const Users = () => {
  return (
    <Container 
      maxWidth="md" 
      sx={{ margin: '20px auto' }}>
      <UsersTable />
    </Container>
  );
}
```
And `Users` inside `App`.

`App.js`
```jsx
import { Users } from './components/users';

export default function App() {
  return <Users />;
}
```
At this moment you should see three table head sections without any data inside the table. It is an expected behaviour.

Now, let's fetch and render data about our users. 

To do this, first of all we need to initialize a `QueryClient` and provide it to the application through a `QueryClientProvider`.

`index.js`
```jsx
import React from "react";
import { createRoot } from "react-dom/client";
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';

import App from "./App";

const queryClient = new QueryClient();

const root = createRoot(document.getElementById("root"));

root.render(
  <QueryClientProvider client={queryClient}>
    <App />
  </QueryClientProvider>
);
```
Then, we need to define a layer responsible for data fetching.

`api/use-users.js`
```jsx
import { useQuery } from "@tanstack/react-query"
import axios from "axios";

export const useUsers = () => {
  return useQuery({
    queryKey: ['users'],
    queryFn: async () => {
     const { data } = await axios.get(
      'https://jsonplaceholder.typicode.com/users'
     );
     return data;
    }
  });
}
```
Here we utilize `useQuery` hook from `React Query` library inside our own custom hook `useUsers`.
* `queryKey: ['users']` is a unique identifier, which is used by React Query to cache and retrieve the query's data.
* `queryFn` is an asynchronous function that defines how to fetch the data. This function will be called by React Query to load the data.

Now, let's use `useUsers` inside our `App` component.

`App.js`
```jsx
import { Users } from './components/users';
import { useUsers } from './api/use-users';

export default function App() {
  const { isFetching, isError, data } = useUsers();

  if (isFetching) {
    return <div>Loading...</div>;
  }

  if (isError) {
    return <div>Error</div>;
  }

  return <Users users={data} />;
}
```

Then, let's handle `users` inside `Users` component and pass it down to `UsersTable`.

`components/users.js`
```jsx
...imports
export const Users = ({ users }) => {
  return (
    <Container 
      maxWidth="md" 
      sx={{ margin: '20px auto' }}>
      <UsersTable users={users} />
    </Container>
  );
}
```
Finally, we can render our `users`.

`components/users-table.js`
```jsx
...imports
export const UsersTable = ({ users }) => {

  const renderUsers = (users) => {
    return users.map(user => {
      return (
        <TableRow key={user.id}>
          <TableCell>{user.name}</TableCell>
          <TableCell>{user.email}</TableCell>
        </TableRow>
      );
    })
  }
  
  return (
    <TableContainer component={Paper}>
      <Table>
        <TableHead>
          <TableRow>
            <TableCell>Name</TableCell>
            <TableCell>Email</TableCell>
          </TableRow>
        </TableHead>
        <TableBody>{renderUsers(users)}</TableBody>
      </Table>
    </TableContainer>
  );
}
```

To be continued...