I am displaying a grid of products with AgGridReact. In this grid I can delete the products, when I delete one the grid update, but if I delete others the grid doesn't update again (but the products are deleted from the database, I need to reload the page to see it). I'm not sure why my useEffect only works with the first deletion.
My component :
import { useState, useEffect, useMemo } from "react";
import { Col, Row, Button } from "react-bootstrap";
import { LinkContainer } from "react-router-bootstrap";
import AdminLinksComponent from "../../../components/admin/AdminLinksComponent";
import { AgGridReact } from "ag-grid-react";
import { redirectionFromAdminPages } from "../../../utils/Redirection";
import "ag-grid-community/styles/ag-grid.css";
import "ag-grid-community/styles/ag-theme-alpine.css";
import { useDispatch } from "react-redux";
const ProductPageComponent = ({ fetchProducts, deleteProduct }) => {
const [productDeleted, setProductDeleted] = useState(false);
const reduxDispatch = useDispatch();
const deleteHandler = (productId) => {
if (
window.confirm(
"Voulez-vous vraiment supprimer ce produit ?\nCette action est irreversible."
)
) {
deleteProduct(productId)
.then(data => {
if (data === "Product deleted") {
setProductDeleted(!productDeleted);
console.log(productDeleted)
window.alert("Produit supprimé !");
} else {
console.error(data);
}
})
.catch(err => console.error(err))
}
};
const editOrDeleteBtn = (data) => {
return (
<>
<LinkContainer to={`/admin/modifier-produit/${data.value}`}>
<Button className="btn-sm">
<i className="bi bi-pencil-square"></i>
</Button>
</LinkContainer>
{" / "}
<Button
className="btn-sm"
variant="danger"
onClick={() => deleteHandler(data.value)}
>
<i className="bi bi-x-circle"></i>
</Button>
</>
);
};
const formatData = (data) => {
const products = [];
const todayDate = new Date();
data.map((product) => {
let productPromotion = "NON";
if (product.promoId) {
const startDate = new Date(product.promoStart);
const endDate = new Date(product.promoEnd);
if (todayDate >= startDate && todayDate <= endDate) {
productPromotion = product.discountRate.toString() + "% (En cours)";
} else if (todayDate < startDate) {
productPromotion = product.discountRate.toString() + "% (A venir)";
} else if (todayDate > endDate) {
productPromotion = product.discountRate.toString() + "% (FINI)";
}
}
let obj = {
reference: product.ref,
name: product.name,
price: Number(product.price),
stock: Number(product.stock),
promotion: productPromotion,
category: product.category,
subcategory: product.subcategory,
editOrDelete: product.id,
};
return products.push(obj);
});
return products;
};
const [rowData, setRowData] = useState([]);
// eslint-disable-next-line
const [colDefs, setColDefs] = useState([
{
field: "reference",
headerName: "#",
width: 80,
},
{
field: "name",
headerName: "Nom",
minWidth: 100,
maxWidth: 180,
flex: 2,
},
{
field: "price",
headerName: "Prix",
width: 100,
},
{
field: "category",
headerName: "Catégorie",
minWidth: 100,
maxWidth: 180,
flex: 2,
},
{
field: "subcategory",
headerName: "Sous-catégorie",
minWidth: 100,
maxWidth: 180,
flex: 2,
},
{
field: "stock",
headerName: "Stock",
width: 90,
},
{
field: "promotion",
headerName: "Promotion",
minWidth: 100,
maxWidth: 180,
flex: 2,
},
{
field: "editOrDelete",
headerName: "Modifier / Supprimer",
width: 150,
filter: false,
cellRenderer: editOrDeleteBtn,
},
]);
const defaultColDef = useMemo(
() => ({
sortable: true,
filter: true,
resizable: true,
editable: true,
}),
[]
);
useEffect(() => {
console.log("useEffect...")
const abctrl = new AbortController();
fetchProducts(abctrl)
.then((data) => formatData(data))
.then((res) => {
setRowData(res)
})
.catch((er) => {
const errorMessage = er.response.data.message
? er.response.data.message
: er.response.data;
redirectionFromAdminPages(errorMessage, reduxDispatch);
});
return () => abctrl.abort();
}, [fetchProducts, productDeleted, reduxDispatch]);
return (
<Row className="my-5 mx-3">
<Col md={2}>
<AdminLinksComponent />
</Col>
<Col md={10} id="admin-products-list-container">
<Row id="admin-products-list-title-row" className="mb-3">
<h1 id="admin-products-list-title">Liste des produits</h1>
<LinkContainer to="/admin/ajouter-produit">
<Button
variant="primary"
size="lg"
id="admin-products-list-create-btn"
>
Ajouter un nouveau produit
</Button>
</LinkContainer>
</Row>
<div id="ag-grid-products-div" className="ag-grid-divs" style={{ height: "603px" }}>
<AgGridReact
style={{ height: "100%" }}
className="ag-theme-alpine"
defaultColDef={defaultColDef}
rowData={rowData}
columnDefs={colDefs}
animateRows={true}
rowSelection={true}
sizeColumnsToFit={true}
pagination={true}
paginationAutoPageSize={true}
/>
</div>
</Col>
</Row>
);
};
export default ProductPageComponent;
The parent:
import ProductPageComponent from "./components/ProductsPageComponent";
import axios from "axios";
const fetchProducts = async (abctrl) => {
const { data } = await axios.get("/api/products/admin", {
signal: abctrl.signal,
});
return data.productList;
};
const deleteProduct = async (productId) => {
const { data } = await axios.delete(`/api/products/admin/${productId}`);
return data;
};
const AdminProductsPage = () => {
return (
<ProductPageComponent
fetchProducts={fetchProducts}
deleteProduct={deleteProduct}
/>
);
};
export default AdminProductsPage;
I tried using React Developper Tools (profiler) but I don't understand the results...
Here is the result from a record with 2 deletions:
Edit: I added a console.log(productDeleted)
in the deleteHandler, it gets console every time I delete a product (even when it doesn't trigger the useEffect), but its value never changes (always returns false).
I also add a console.log("useEffect...")
at the beginning of the useEffect hook, which shows the hook is not triggered when I delete more than one product.
If someone has the same probleme, here is the solution :
In the deleteHandler function, instead of triggering the useEffect to refetch all data, I delete the row using ag-grid api + I order the server to remove data in the db. I also deleted the productDeleted
state variable.
Here is what I changed in my code:
const deleteHandler = (rowData) => {
if (
window.confirm(
"Voulez-vous vraiment supprimer ce produit ?\nCette action est irreversible."
)
) {
rowData.api?.applyTransaction({ remove: [rowData.data] });
deleteProduct(rowData.value)
.then(data => {
if (data === "Product deleted") {
window.alert("Produit supprimé !");
} else {
console.error(data);
}
})
.catch(err => console.error(err));
}
};
const editOrDeleteBtn = (rowData) => {
return (
<>
<LinkContainer to={`/admin/modifier-produit/${rowData.value}`}>
<Button className="btn-sm">
<i className="bi bi-pencil-square"></i>
</Button>
</LinkContainer>
{" / "}
<Button
className="btn-sm"
variant="danger"
onClick={() => deleteHandler(rowData)}
>
<i className="bi bi-x-circle"></i>
</Button>
</>
);
};