i'm having problems with this component because it renders 5 times and I don't know why exactly, I've been reading about and it could be because of the RTK - query but I'm not sure how to fix it. I'll appreciate any help.
import { memo, useCallback, useEffect, useMemo, useState } from "react";
import { DataGrid, GridToolbarContainer } from "@mui/x-data-grid";
import {
Alert,
Box,
Button,
Modal,
Snackbar,
TextField,
Typography,
} from "@mui/material";
import { Add } from "@mui/icons-material";
import {
useAddCategoryMutation,
useEditCategoryMutation,
useGetCategoriesQuery,
} from "../store/management/managementApi";
import { useForm } from "../hooks/useForm";
const EditToolbar = memo(({ setIsModalOpen }) => {
const handleClick = () => {
setIsModalOpen(true);
};
return (
<GridToolbarContainer>
<Button color="primary" startIcon={<Add />} onClick={handleClick}>
Add Category
</Button>
</GridToolbarContainer>
);
});
const initialForm = { name: "" };
export const CategoriesTable = () => {
const { data, isLoading, isError } = useGetCategoriesQuery();
const [editCategory, { error: editError }] = useEditCategoryMutation();
const [addCategory, { error: addError }] = useAddCategoryMutation();
const { name, onInputChange, onResetForm } = useForm(initialForm);
const [rows, setRows] = useState([]);
const [snackbar, setSnackbar] = useState(null);
const [isModalOpen, setIsModalOpen] = useState(false);
console.log(data);
// Snackbar
const handleCloseSnackbar = () => setSnackbar(null);
// rows and colums
const memoizedRows = useMemo(() => rows, [data?.content]);
useEffect(() => {
if (!isLoading && !isError && data !== undefined) {
console.log("yes");
setRows(data.content);
}
}, [data]);
const colums = [
{ field: "name", headerName: "NAME", flex: 1, editable: true },
];
// Table functions
const processRowUpdate = useCallback(
async (newRow, oldRow) => {
if (newRow.name.trim() === oldRow.name) return oldRow;
try {
const response = await editCategory(newRow);
if (response.data?.ok) {
setSnackbar({
children: "Changes saved successfully",
severity: "success",
});
return response.data.content;
} else {
setSnackbar({ children: response.error.data.msg, severity: "error" });
return oldRow;
}
} catch (error) {
setSnackbar({
children: editError.data.msg,
severity: "error",
});
return oldRow;
}
},
[editCategory]
);
// Modal functions
const handleSubmit = useCallback(
async (event) => {
event.preventDefault();
try {
const response = await addCategory({ name: name });
if (response.data?.ok) {
const newRows = [...rows, response.data.content];
setRows(newRows);
setSnackbar({
children: "Category saved successfully",
severity: "success",
});
setIsModalOpen(false);
onResetForm();
} else {
setSnackbar({ children: response.error.data.msg, severity: "error" });
setIsModalOpen(false);
onResetForm();
}
} catch (error) {
setSnackbar({
children: addError.data.msg,
severity: "error",
});
}
},
[addCategory, name, onResetForm, rows]
);
const handleCloseModal = () => {
setIsModalOpen(false);
onResetForm();
};
const handleCancel = () => {
setIsModalOpen(false);
onResetForm();
};
return (
<Box
sx={{
height: 500,
width: "100%",
"& .actions": {
color: "text.secondary",
},
"& .textPrimary": {
color: "text.primary",
},
}}
>
<Typography
variant="h4"
textTransform="uppercase"
sx={{
width: "100%",
textAlign: "center",
mb: "16px",
}}
>
Categories
</Typography>
<DataGrid
rows={rows}
columns={colums}
loading={isLoading}
processRowUpdate={processRowUpdate}
slots={{
toolbar: EditToolbar,
}}
slotProps={{
toolbar: { setIsModalOpen },
}}
sx={{ textTransform: "capitalize" }}
/>
{!!snackbar && (
<Snackbar
open
anchorOrigin={{
vertical: "top",
horizontal: "center",
}}
onClose={handleCloseSnackbar}
autoHideDuration={6000}
>
<Alert
{...snackbar}
variant="filled"
sx={{
color: "white",
}}
onClose={handleCloseSnackbar}
/>
</Snackbar>
)}
<Modal open={isModalOpen} onClose={handleCloseModal}>
<Box
sx={{
position: "absolute",
top: "50%",
left: "50%",
transform: "translate(-50%, -50%)",
width: 400,
bgcolor: "background.paper",
border: "2px solid #000",
boxShadow: 24,
p: 4,
}}
>
<Typography textTransform="uppercase">Add Category</Typography>
<Box component="form" onSubmit={handleSubmit} sx={{ mt: 1 }}>
<TextField
margin="normal"
required
fullWidth
id="name"
label="Category Name"
name="name"
value={name}
onChange={onInputChange}
/>
<Box
sx={{
width: "100%",
display: "flex",
justifyContent: "space-between",
gap: "8px",
}}
>
<Button
variant="outlined"
fullWidth
sx={{ mt: 3, mb: 2 }}
onClick={handleCancel}
>
Cancel
</Button>
<Button
type="submit"
variant="contained"
fullWidth
sx={{ mt: 3, mb: 2 }}
>
Save
</Button>
</Box>
</Box>
</Box>
</Modal>
</Box>
);
};
I've tried to move the query to the parent component but it made this one to have the same issue. they are exact 5 renders. Chrome Dev Tools
this is my rtk-query's endpoints configuration:
import { createApi, fetchBaseQuery } from "@reduxjs/toolkit/query/react";
import { getEnvVariables } from "../../helpers/getEnvVariables";
const { VITE_API_URI } = getEnvVariables();
export const managementApi = createApi({
reducerPath: "management",
baseQuery: fetchBaseQuery({
baseUrl: `${VITE_API_URI}/management`,
prepareHeaders: (headers) => {
const token = localStorage.getItem("token");
if (token) {
headers.set("x-token", token);
}
return headers;
},
}),
tagTypes: ["Categories", "Products"],
endpoints: (build) => ({
getCategories: build.query({
query: () => "/categories",
providesTags: (result) =>
result
? [
...result.content.map(({ id }) => ({ type: "Categories", id })),
{ type: "Categories", id: "LIST" },
]
: [{ type: "Categories", id: "LIST" }],
}),
addCategory: build.mutation({
query: (body) => ({
url: "/categories/new",
method: "POST",
body,
}),
invalidatesTags: [{ type: "Categories", id: "LIST" }],
}),
editCategory: build.mutation({
query: (body) => ({
url: `categories/${body.id}`,
method: "PUT",
body,
}),
invalidatesTags: (result, error, { id }) => [{ type: "Categories", id }],
}),
deleteCategory: build.mutation({
query: (id) => ({
url: `/categories/${id}`,
method: "DELETE",
}),
invalidatesTags: (result, error, id) => [{ type: "Categories", id }],
}),
}),
});
export const {
useGetCategoriesQuery,
useAddCategoryMutation,
useEditCategoryMutation,
useDeleteCategoryMutation,
} = managementApi;
For this app I'm using Reactjs, Redux-Tool-kit y materialUI