child calling parent function not working properly react - reactjs

suppose i have a product data for sales which i put in a state
const [products, setProducts] = useState([])
products= {
'product_id':'1',
'total':'100'
}
setProducts(products)
i send this data to a view products component
<SalesViewProducts
products={products}
>
</SalesViewProducts>
where the view products, SalesViewProducts, component looks something like
const SalesViewProducts = (props) => {
const dispatch = new useDispatch();
const [inventoryData, setInventoryData] = useState([]);
const [selectedProducts, setSelectedProducts] = useState();
const [show, setShow] = useState(false);
useEffect(() => {
// var total = 0;
// selectedProducts.forEach(element => {
// total+=element.total
// });
// props.calculateTotal(total)
});
const addProducts = (data) => {
console.log(props.products);
};
return (
<div>
<Button
variant="primary"
size="lg"
onClick={() => console.log(props.products)}
// or
onClick={()=>setShow(true)}
style={{ width: "100%", marginBottom: "1rem" }}
>
ADD PRODUCTS
</Button>
<Table striped bordered hover variant="light">
</Table>
<InventoryModal
modalShow={show}
modalSetShow={setShow}
addProducts={addProducts}
></InventoryModal>
</div>
);
};
export default SalesViewProducts;
emphasizing on the button "ADD PRODUCTS", when the onclick function of that button is just console.log(props.products), i get the data just fine from the props.
my issue now comes is this button is actually supposed to open a modal, in which that modal InventoryModal has a whole table of products in inventory and a button that calls a function in the SalesViewProducts component.
const InventoryModal = (props) => {
const dispatch = new useDispatch();
const gridRef = useRef();
const [show, setShow] = useState(props.modalShow);
const handleClose = () => props.modalSetShow(false);
const [rowData, setRowData] = useState([]);
const [columnDefs] = useState([
{ field: "id", width: 100, headerName: "ID" },
{
field: "product_name",
width: 244,
headerName: "Name",
wrapText: true,
autoHeight: true,
},
{ field: "product_price", width: 110, headerName: "Price" },
{ field: "in_stock", width: 110, headerName: "Stock" },
{
field: "action",
cellRenderer: (params) => (
<strong>
<Button
variant="success"
size="sm"
onClick={() => {
// console.log(params.data.product_name)
var data = {
product_id: params.data.id,
quantity: 1,
price: 0,
total: 0,
name: params.data.product_name,
};
props.addProducts(data);
}}
>
Add
</Button>
</strong>
),
cellRendererParams: {
clicked: function (field) {
alert(`${field} was clicked`);
},
},
},
]);
useEffect(() => {
dispatch(getAllInventoryAction())
.unwrap()
.then((data) => {
setRowData(data.inventoryData);
});
}, []);
return (
<Modal show={props.modalShow} onHide={handleClose} size="lg">
<Modal.Header closeButton>
<Modal.Title>Select Products</Modal.Title>
</Modal.Header>
<Modal.Body>
<div className="ag-theme-alpine" style={{ height: 650, width: "100%" }}>
<AgGridReact
ref={gridRef}
rowData={rowData}
columnDefs={columnDefs}
pagination={true}
// rowHeight={100}
></AgGridReact>
</div>
</Modal.Body>
</Modal>
);
};
export default InventoryModal;
now when i click the add button in the inventory modal, supposedly its supposed to call the parents addProducts function which console.log(props.products). but the result of calling this function is [], why is this so? wheh i call the addProducts function from within the component everything works fine, but when i call the addproducts function from the child component everything goes wrong.

ive solved this problem after realizing the issue was with aggridreact. i had to pass my function to context and then call it through context for it to work. im referencing this stackoverflow question that helped me
react functional component with ag grid cannot call parent function via context
<AgGridReact
ref={gridRef}
rowData={rowData}
columnDefs={columnDefs}
pagination={true}
context={props}
// rowHeight={100}
>
</AgGridReact>
setColumn([
{ field: 'id', width: 100, headerName:"ID"},
{ field: 'product_name',width:244, headerName: 'Name',wrapText: true, autoHeight: true, },
{ field: 'product_price',width:110, headerName: 'Price' },
{ field: 'in_stock',width:110, headerName: 'Stock' },
{ field: 'action',
cellRenderer: (params) => (
<strong>
<Button
variant="success"
size="sm"
onClick={()=>{
params.context.products()
}}
>
Add
</Button>
</strong>
),
},
])

Related

MUI Data Grid Editable and TextFields cannot edit or focused when inside MUI Dialog

Currently working with React project using Material UI framework.
I use MUI Data-Grid together with MUI Dialog.
The problem is I can't edit any Text Fields or Input fields that are inside the Dialog.
I also tried adding disableAutoFocus and disableEnforceFocus but nothing works
Is there any solution for this? Tried searching but nothing works for my case.
Here's my current code.
const ShopDialog = forwardRef((props, ref)=> {
const [open, setOpen] = React.useState(false);
const [products, setProducts] = React.useState([]);
useImperativeHandle(ref, () => ({
buyProduct(products) {
setProducts(products);
setOpen(true);
}
}));
const handleClose = () => {
setOpen(false);
};
const columns = [
{ field: 'uuid', headerName: 'UUID', width: 150 },
{
field: 'name',
headerName: 'Product name',
width: 320,
},
{
field: 'category',
headerName: 'Category',
width: 200,
},
{
field: 'amount',
headerName: 'Amount',
type: 'number',
width: 150,
editable: true,
valueSetter: (params)=>{
let value = parseFloat(params.value);
if(isNaN(value) || value < 0){
return {...params.row}
}else{
return {...params.row, amount: value || ''}
}
}
},
];
return (
<Dialog
onClose={handleClose}
aria-labelledby="customized-dialog-title"
open={open}
disableAutoFocus
disableEnforceFocus
maxWidth='md'
fullWidth
>
<DialogContent dividers sx={{padding: 0, height: 500 }}>
<DataGrid
getRowId={(row)=>row.uuid}
rows={products}
columns={columns}
experimentalFeatures={{ newEditingApi: true }}
/>
</DialogContent>
<DialogActions>
<Button variant='contained'
size='small'
onClick={handleClose}>
Cancel
</Button>
<Button variant='contained'
size='small'
onClick={handleSubmit}
>
Submit
</Button>
</DialogActions>
</Dialog>
);
})

Material UI DataGrid params only passing last value in the table

I have a Data Grid table of users, the last column of the table is meant to be a sub-menu of options that each open up a modal. However, when I try to pass the data of a given row to a modal, it will only pass the final row of the data set (i.e. if I have 10 users, it passes rowId:10).
export default function Users() {
const [userData, setUserData] = React.useState([]);
React.useEffect(() => {
let apiClient = new APIClient();
apiClient.getUser().then((response) => {
setUserData(response);
console.log(response);
});
}, []);
const [anchorEl, setAnchorEl] = React.useState<null | HTMLElement>(null);
const [showEditModal, setShowEditModal] = React.useState<boolean>(false);
const [showDeleteModal, setShowDeleteModal] = React.useState<boolean>(false);
//for each modal
const open = Boolean(anchorEl);
const moreOptions = (event: React.MouseEvent<HTMLButtonElement>) => {
setAnchorEl(event.currentTarget);
};
const handleClose = () => {
setAnchorEl(null);
};
const columns: GridColDef[] = [
{ headerName: 'Name', field: 'name', flex: 1 },
{ headerName: 'Email', field: 'email', flex: 1 },
{ headerName: 'Date Added', field: 'added', flex: 0.7 },
{ headerName: 'Reports To', field: 'reports_to', flex: 1 },
{
field: ' ',
flex: 0.2,
renderCell: (params) => {
return (
<>
<IconButton
id="basic-button"
aria-controls={open ? 'basic-menu' : undefined}
aria-haspopup="true"
aria-expanded={open ? 'true' : undefined}
onClick={moreOptions}>
<MoreIcon />
</IconButton><Menu
id="basic-menu"
anchorEl={anchorEl}
open={open}
onClose={handleClose}
MenuListProps={{
'aria-labelledby': 'basic-button',
}}
>
<MenuItem>
<UserNotification />
</MenuItem>
<MenuItem onClick={() => { setShowEditModal(true); handleClose(); } }>
<UserEdit />
</MenuItem>
<MenuItem onClick={() => { setShowDeleteModal(true); handleClose(); } }>
<UserDelete />
</MenuItem>
</Menu>
<EditUserModal userData={params.row} show={showEditModal} toggleModal={(value) => { setShowEditModal(value); } } />
<DeleteUserModal userData={params.row} show={showDeleteModal} toggleModal={(value) => { setShowDeleteModal(value); } } />
</>
);
}
},
];
return (
<div style={{ height: 'auto', width: "100%" }}>
<StripedDataGrid rows={userData} columns={columns}
getRowClassName={(params) =>
params.indexRelativeToCurrentPage % 2 === 0 ? 'even' : 'odd'
}
hideFooter={true}
autoHeight={true}
components={{ Toolbar: GridToolbar }}
/>
</div>
);
}
The Data Grid documentation does not have a clear guide on how to pass props of the grid forward. Attempting to populate userData instead of params gives me an overload error. What is the proper way to handle this?

unable to handle multiple checks in Nested Table of Ant Design

check this code. when i check from one table it works properly.but when i check options from multiple tables it removes the data of other tables and only show current tables checked data,forEg :in my logic part when i select multiple checkbox from one table it uncheck all other tables
import axios from "axios";
import React, { useState, useEffect } from "react";
import permissions from "../Data/PermissionAPI.json";
import modules from "../Data/ModuleAPI.json";
import { Table, Row, Col, Form, Button, Checkbox } from "antd";
const TEsting = () => {
const [form] = Form.useForm();
const dData=[];
const [data, setData] = useState([]);
const [selectedRowKeys, setSelectedRowKeys] = useState([]);
const rowSelection = {
selectedRowKeys: selectedRowKeys,
onChange: (selectedRowKeys, selectedRows) => {
setSelectedRowKeys(selectedRowKeys);
console.log(`selectedRowKeys: ${selectedRowKeys}`, 'selectedRows: ', selectedRows);
},
};
const Permissions = ({ moduleid }) => {
const perm = permissions.filter((item) => item.moduleid === moduleid);
return (
<Table
style={{ width: "100%" }}
pagination={false}
rowSelection={rowSelection}
rowKey={(record) => record.id}
dataSource={perm}
columns={columns}
/>
);
};
const handleSubmit = (values) => {
console.log("Submit Pressed");
};
const columns = [
{
dataIndex: "key",
key: "key",
},
{
title: "Permission",
dataIndex: "pname",
key: "pname",
},
{
title: "Description",
dataIndex: "pdesc",
key: "pname",
},
];
const DisplayModules = modules.map((item, index) => {
const module = item;
// console.log(module);
if (module === undefined) return false;
return (
<Col xxl={12} xl={12} xs={24} key={index}>
{data}
<div
style={{
backgroundColor: "#ffe8c2",
padding: 20,
margin: "20px 20px 20px 20px",
borderRadius: "10px",
}}
title={`${module.id} - ${module.modulename}`}
>
<div className="about-project">
<Permissions moduleid={module.id} />
</div>
</div>
</Col>
);
});
useEffect(() => {
setData(dData);
console.log(data);
}, []);
return (
<div style={{ backgroundColor: "#e6e6e6" }}>
<Form
style={{ width: "100%" }}
form={form}
name="editSite"
onFinish={handleSubmit}
>
<Row gutter={25}>
{DisplayModules}
<div className="add-form-action">
<Form.Item>
<Button size="large" htmlType="submit" type="primary" raised>
Save
</Button>
</Form.Item>
</div>
</Row>
</Form>
</div>
);
};
export default TEsting;
here what i want:i want checkbox Data from multiple tables in one state without empty previous state,it would be very helpful if you help in this.
you need to decide - what data structure you want to get at the end, after user makes his choices and checks the checkboxes:
is that something like -
{
"moduleId": "1",
"permissions": [1,4],
"moduleId": "2",
"permissions": []
}
?
Try this solution
const [selectedRowKeys, setSelectedRowKeys] = useState([]);
const handleSelect = (record, selected) => {
if (selected) {
setSelectedRowKeys((ids) => [...ids, record.id]);
} else {
setSelectedRowKeys((ids) => {
const index = ids.indexOf(record.id);
return [...ids.slice(0, index), ...ids.slice(index + 1)];
});
}
};
const rowSelection = {
selectedRowKeys: selectedRowKeys,
onSelect: handleSelect,
};

Putting two functions in the same "onclick"

I'm trying to display a group of users in a table, and I'm using a library called "ReactTable", and I have an "approve" button, when the user clicks on the "approve" button two things should happen:
The first will be a dispatch of the approveUser function
The second thing, a message will appear that "acceptance has been completed".
The problem is that I want to put two functions in the same button and the same "onClick", and when I did that the site was no longer working,
How can I solve the problem?
function Alert(props) {
return <MuiAlert elevation={6} variant="filled" {...props} />;
}
const useStyles = makeStyles({
button1: {
backgroundColor: "none",
"&:hover": {
backgroundColor: "#43a047",
color: "#e8e4e4",
transition: "0.3s",
borderColor: "#43a047",
},
},
button2: {
backgroundColor: "none",
"&:hover": {
backgroundColor: "#e53935",
color: "#e8e4e4",
transition: "0.3s",
borderColor: "#e53935",
},
},
});
function ContactsList(props) {
const classes = useStyles();
const dispatch = useDispatch();
const [open, setOpen] = React.useState(false);
const handleClick = () => {
setOpen(true);
};
const handleClose = (event, reason) => {
if (reason === "clickaway") {
return;
}
setOpen(false);
};
useEffect(() => {
dispatch(getUsersRequests());
}, [dispatch]);
const usersRequestsState = useSelector(
(state) => state.usersRequestsApp["usersRequests"]
);
console.log(
"requests inside Contacts List: ",
usersRequestsState["usersRequests"]
);
const columns = useMemo(
() => [
{
Header: "",
// this is function or property
accessor: "avatar",
Cell: ({ row }) => {
return (
<Avatar
className="mx-8"
alt={row.original.name}
src={row.original.avatar}
style={{ height: "7rem", width: "7rem" }}
/>
);
},
className: "justify-center",
width: 64,
sortable: false,
},
{
Header: "First Name",
accessor: "firstName",
className: "font-medium",
sortable: true,
},
{
Header: "Last Name",
accessor: "lastName",
className: "font-medium",
sortable: true,
},
{
Header: "Email",
accessor: "email",
sortable: true,
},
{
Header: "Phone Number",
accessor: "phoneNumber",
sortable: true,
},
{
Header: "actions",
accessor: "",
sortable: true,
id: "action",
// width: 100,
Cell: ({ row }) => (
<div className="flex items-center">
<ButtonGroup
style={{
maxWidth: "206px",
maxHeight: "40px",
minWidth: "206px",
minHeight: "40px",
}}
aria-label="outlined primary button group"
>
<Button
style={{
maxWidth: "100px",
minWidth: "100px",
}}
onClick={(ev) => {
ev.stopPropagation();
handleClick;
dispatch(approveUser(row.original.id));
}}
className={classes.button1}
>
approve
</Button>
<Snackbar
open={open}
autoHideDuration={6000}
onClose={handleClose}
>
<Alert onClose={handleClose} severity="success">
acceptance has been completed!
</Alert>
</Snackbar>
<Button
style={{
maxWidth: "100px",
minWidth: "100px",
}}
onClick={(ev) => {
ev.stopPropagation();
dispatch(rejectUser(row.original.id));
}}
className={classes.button2}
>
reject
</Button>
</ButtonGroup>
</div>
),
},
],
[]
);
const dataResponse = useMemo(() => usersRequestsState["data"]);
console.log("dataResponse: ", dataResponse);
return (
<motion.div
initial={{ y: 20, opacity: 0 }}
animate={{ y: 0, opacity: 1, transition: { delay: 0.2 } }}
>
<ContactsTable columns={columns} data={dataResponse} />
</motion.div>
);
}
export default ContactsList;
There is no use of just putting a function reference and not using it
onClick={(ev) => {
ev.stopPropagation();
handleClick;
dispatch(approveUser(row.original.id));
}}
You should invoke the function by putting () at the end of the handleClick as:
onClick={(ev) => {
ev.stopPropagation();
handleClick(ev); // INVOCATION
dispatch(approveUser(row.original.id));
}}
This is just personal preference
You are handling onClick inline and trying to invoke the function handleClick, so It would be much cleaner if you could just set the state of Open inside the handler.
setOpen(true);
Below code is much more readable
onClick={(ev) => {
ev.stopPropagation();
setOpen(true);
dispatch(approveUser(row.original.id));
}}
When you call the function you need to use ()
onClick={(ev) => {
ev.stopPropagation();
handleClick();
dispatch(approveUser(row.original.id));
}}

How to delete multiple selected rows in Material-UI DataGrid?

I wanna know how to delete rows from the DataGrid from Material-UI by using the checkboxes in React. I haven't find any proper tutorial for doing this on DataGrid although I found one for MaterialTable but is not the same.
Any help is welcome.
UPDATE
My full code after adapt solution:
import React, { useState, useEffect, Fragment } from 'react'
import {db} from './firebase';
import { useHistory, useLocation } from 'react-router-dom';
import "./ListadoEstudiantes.css"
import * as locales from '#mui/material/locale';
import { DataGrid,
GridRowsProp, GridColDef,
GridToolbarContainer, GridToolbarColumnsButton, GridToolbarFilterButton, GridToolbarExport, GridToolbarDensitySelector} from '#mui/x-data-grid';
import { Button, Container } from "#material-ui/core";
import { IconButton} from '#mui/material';
import PersonAddIcon from '#mui/icons-material/PersonAddSharp';
import DeleteOutlinedIcon from '#mui/icons-material/DeleteOutlined';
import { Box } from '#mui/system';
function ListadoEstudiantes({user}) {
const history = useHistory("");
const crearEstudiante = () => {
history.push("/Crear_Estudiante");
};
const [estudiantesData, setEstudiantesData] = useState([])
const parseData = {
pathname: '/Crear_Pedidos',
data: estudiantesData
}
const realizarPedidos = () => {
if(estudiantesData == 0)
{
window.alert("Seleccione al menos un estudiante")
}
else {
history.push(estudiantesData);
}
};
function CustomToolbar() {
return (
<GridToolbarContainer>
<GridToolbarFilterButton />
<GridToolbarDensitySelector />
</GridToolbarContainer>
);
}
const [estudiantes, setEstudiantes] = useState([]);
const [selectionModel, setSelectionModel] = useState([]);
const columns = [
{ field: 'id', headerName: 'ID', width: 100 },
{field: 'nombre', headerName: 'Nombre', width: 200},
{field: 'colegio', headerName: 'Colegio', width: 250},
{field: 'grado', headerName: 'Grado', width: 150},
{
field: "delete",
width: 75,
sortable: false,
disableColumnMenu: true,
renderHeader: () => {
return (
<IconButton
onClick={() => {
const selectedIDs = new Set(selectionModel);
setEstudiantes((r) => r.filter((x) => !selectedIDs.has(x.id)));
}}
>
<DeleteOutlinedIcon />
</IconButton>
);
}
}
];
const estudiantesRef = db.collection("usuarios").doc(user.uid).collection("estudiantes")
useEffect(() => {
estudiantesRef.onSnapshot(snapshot => {
const tempData = [];
snapshot.forEach((doc) => {
const data = doc.data();
tempData.push(data);
});
setEstudiantes(tempData);
})
}, []);
return (
<Container fixed>
<Box mb={5} pt={2} sx={{textAlign:'center'}}>
<Button
startIcon = {<PersonAddIcon />}
variant = "contained"
color = "primary"
size = "medium"
onClick={crearEstudiante} >
Crear Estudiantes
</Button>
<Box pl={25} pt={2} sx={{height: '390px', width: "850px", textAlign:'center'}}>
<DataGrid
rows={estudiantes}
columns={columns}
pageSize={5}
rowsPerPageOptions={[5]}
components={{
Toolbar: CustomToolbar,
}}
checkboxSelection
//Store Data from the row in another variable
onSelectionModelChange = {(id) => {
setSelectionModel(id);
const selectedIDs = new Set(id);
const selectedRowData = estudiantes.filter((row) =>
selectedIDs.has(row.id)
);
setEstudiantesData(selectedRowData)
console.log(estudiantesData);
}
}
{...estudiantes}
/>
</Box></Box></Container>
)
}
export default ListadoEstudiantes
UPDATE
Everything works! thank you
You can keep track of the currently selected IDs via selectionModel/onSelectionModelChange props, and perform the necessary action when the user click the IconButton on the header. Because renderHeader callback doesn't provide the selection state, I have to make use of closure by putting the columns definition inside the function body so I can reference selectionModel in the callback:
const [rows, setRows] = React.useState(_rows);
const [selectionModel, setSelectionModel] = React.useState([]);
const columns: GridColDef[] = [
{ field: "col1", headerName: "Column 1", width: 150 },
{ field: "col2", headerName: "Column 2", width: 150 },
{
field: "delete",
width: 75,
sortable: false,
disableColumnMenu: true,
renderHeader: () => {
return (
<IconButton
onClick={() => {
const selectedIDs = new Set(selectionModel);
// you can call an API to delete the selected IDs
// and get the latest results after the deletion
// then call setRows() to update the data locally here
setRows((r) => r.filter((x) => !selectedIDs.has(x.id)));
}}
>
<DeleteIcon />
</IconButton>
);
}
}
];
return (
<div style={{ height: 400, width: "100%" }}>
<DataGrid
rows={rows}
columns={columns}
checkboxSelection
onSelectionModelChange={(ids) => {
setSelectionModel(ids);
}}
/>
</div>
);

Resources