how can I put elements of an array in a checkbox? - reactjs

I am using react hooks and have a dynamically filled array of values ​​after a backend call. How can I then insert these values ​​(which I don't know a priori) in a checkbox?
I get some properties from another component.
These properties change based on a selection from a table.
I would like to be able to add / remove these properties based on a selection on a checkbox when I open the "Available roles list"
const UsersList = (props) => {
const { loadedRoles } = props;
const [checked, setChecked] = useState([]);
const selectHandler = (Event, selected) => {
loadedRoles(selected.roles);
};
const handleChange = (Event) => {
setChecked({ ...props.roles, [Event.target.name]: Event.target.checked });
};
return (
<div>
<MaterialTable
title=" Users List"
data={props.users}
columns={[
{ title: "User Id", field: "id" },
{ title: "Active", field: "active" },
{ title: "System", field: "system" },
{ title: "Roles", field: "roles" },
]}
options={{
cellStyle: {
width: 100,
minWidth: 100,
height: 1,
},
headerStyle: {
width: 100,
minWidth: 100,
},
}}
onRowClick={selectHandler}
/>
<Card>Current Role: {props.current}</Card>
<Card>
<label>Available Roles: {props.roles}</label>
<Checkbox name={props.roles} onChange={handleChange} />
</Card>
</div>

I can assume that you receive from your backend an array of objects, containing the label, keys, and values for the checkboxes?
[
{ label: 'Checkbox #1', key: 'checkbox1', checked: true },
{ label: 'Checkbox #2', key: 'checkbox2', checked: false },
{ label: 'Checkbox #3', key: 'checkbox3', checked: true },
]
You can make the call to the API in a useEffect, store the result in a local state via useState, then iterate through the values in the rendering:
const [checkboxes, setCheckboxes] = useState(null)
useEffect(() => {
fetch('/your/api')
.then(res => res.json())
.then(checkboxes => setCheckboxes(checkboxes))
}, [])
return (
<ul>
{checkboxes === null
? <li>Loading...</li>
: checkboxes.map(checkbox => (
<li key={checkbox.key}>
<label>
<input type="checkbox" name={key} defaultChecked={checkbox.checked} />
{checkbox.label}
</label>
</li>
))
}
</ul>
)
The checkboxes here are not controlled (defaultChecked). To use controlled checkboxes instead, you’ll need to create another local state and initialize it from the values returned by the backend, and use checked & onChange props instead.

Related

How can I remove column filters from a table in Antdesign on programmatical removal of columns?

In a ReactJS application (Typescript), using Antdesign 4.12.2, I am building a table that shows a list of record data over a number of columns. The columns shown can be modified by adding or removing them through a popover with a tree containing the various options.Columns may have associated filters.
It all works well, until you activate a filter and then remove the column. The column is then removed, but the filter is not deactivated, which means that the number of records shown does not go back to the number from before the filter was activated. When re-adding the column, the filter previously activated is still active.
How do I make sure that when a column is removed, any active filters are disabled?
Below is my code.
Note that as per this suggestion I've tried changing the filteredValue of a column to null, but does not appear to have any effect in the useEffect. Interestingly, using the 'clear' button, that I've added for debugging, does seem to work to a degree. It appears to remove all filters and shows all records after that. This only works when the column is visible, however. It does nothing after the column has been removed. Also, if you remove the column and re-add it after this, the filter will appear to work again. But if you re-added it after leaving the filters deselected, it will come back activated with the original filter selected. It's a hot mess!
Edit: I've updated the code below to also track and update filteredInfo, as per this article.
const isPresent = <T,>(t: T | undefined | null): t is T => t != null;
interface User {
username: string,
status: string
}
const ReportItemsTable: React.FC = () => {
const allColumnOptions = [
{
key: "username", title: "Username"
},
{
key: "status", title: "Status"
}
];
const items: User[] = [
{
status: "one",
username: "Piet"
},
{
status: "two",
username: "Saar"
},
{
status: "one",
username: "Jan"
},
{
status: "two",
username: "Klaas"
},
{
status: "one",
username: "Maartje"
},
{
status: "three",
username: "Sjeng"
},
{
status: "one",
username: "Elke"
},
{
status: "two",
username: "Twan"
},
{
status: "one",
username: "An"
},
{
status: "three",
username: "Nel"
},
];
const [filteredInfo, setFilteredInfo] = useState<Record<string, FilterValue | null>>({});
const possibleColumns: ColumnType<User>[] = [
{
title: "Username",
key: "username",
dataIndex: "username",
sorter: (a: User, b: User) => a.username.localeCompare(b.username),
render: (userName: string) => (<>{userName}</>)
},
{
title: "Status",
key: "status",
dataIndex: "status",
filteredValue: filteredInfo && filteredInfo["status"],
filters: [
{
text: "One",
value: "one"
},
{
text: "Two",
value: "two"
},
{
text: "Three",
value: "three"
},
],
onFilter: (value: ColumnFilterValue, user: User) => user.status === value,
sorter: (a: User, b: User) => a.status.localeCompare(b.status),
render: (status: string) => (<>{status}</>)
}
];
// Note that selectedColumns is controlled by checkedKeys. selectedColumns should not be modified directly.
const [checkedKeys, setCheckedKeys] = useState<Key[] | { checked: Key[]; halfChecked: Key[]; }>([]);
const [selectedColumns, setSelectedColumns] = useState<ColumnType<User>[]>(possibleColumns);
const getTreeData = (allOptions: DataNode[]): DataNode[] => {
return [{
key: "masterKey",
title: "(de)select all",
children: [...allOptions]
}];
};
// The useEffect below was originally only in charge of checking
// whether the checkedKeys had changed and accordingly adding or
// removing columns from the table. It was expanded to set the
// filteredValue to null if the column was unchecked.
useEffect(() => {
setFilteredInfo(filteredInfo => {
const newFilteredInfo = {...filteredInfo};
const visibleKeys = Array.isArray(checkedKeys) ? checkedKeys : checkedKeys.checked;
const invisibleKeys = possibleColumns.map(column => column.key).filter(isPresent).filter(columnKey => !visibleKeys.includes(columnKey));
invisibleKeys.forEach(key => delete newFilteredInfo[key]);
return newFilteredInfo;
});
if (Array.isArray(checkedKeys)) {
setSelectedColumns(possibleColumns
.map(column => {
if (!isPresent(column.key)) return column;
if (!checkedKeys.includes(column.key)) {
return {
...column,
filteredValue: null
}
}
return column;
})
.filter(column => checkedKeys
.map(checkedKey => checkedKey.toString())
.includes(column.key?.toString() ?? "")));
} else {
setSelectedColumns(possibleColumns
.map(column => {
if (!isPresent(column.key)) return column;
if (!checkedKeys.checked.includes(column.key)) {
return {
...column,
filteredValue: null
}
}
return column;
})
.filter(column => checkedKeys.checked
.map(checkedKey => checkedKey.toString())
.includes(column.key?.toString() ?? "")));
}
}, [checkedKeys]);
const filterTree =
<Tree
key={"columnfilter"}
checkable
defaultExpandedKeys={["masterKey"]}
checkedKeys={checkedKeys}
selectable={false}
onCheck={checkedKeys => {
setCheckedKeys(checkedKeys);
}}
treeData={getTreeData(allColumnOptions)}
/>
;
const columnFilterButton =
<Popover
placement="bottom"
title={"Select columns"}
content={filterTree}
trigger="click">
<Tooltip title={"Select columns"}>
<Button icon={<ViewWeekOutlinedIcon sx={{ fontSize: 17, marginTop: "2.5px" }}></ViewWeekOutlinedIcon>}>
</Button>
</Tooltip>
</Popover>
;
return (
<>
<Row style={{paddingTop: "10px", paddingBottom: "10px"}}>
<Col span={24} style={{ textAlign: "left" }}>
<Space>
<Button.Group>
{columnFilterButton}
</Button.Group>
</Space>
</Col>
</Row>
<Table
onChange={(pagination, filters) => setFilteredInfo(filters)}
columns={selectedColumns}
dataSource={items}
size="middle"
/>
<button onClick={() => {
setFilteredInfo({});
setSelectedColumns(selectedColumns.map(column => {
return {
...column,
filteredValue: null
};
}));
}
}>clear</button>
</>
);
};
export default ReportItemsTable;

If Statement Inside Map

I currently have a DataGrid that is rendering data grabbed from my backend mongoDB. The data rendering is mapped to keys specified by the objects in the mongoDB document. In each document is a set of boolean values, and I am trying to check if any of those are true, if they are true it will render a Y in the repairsNeeded column for each row, if not, it will render an N. The main problem I am running into is where/how to check this. I have played with a few different ideas to no avail. Right now I have the repairsNeeded column for each row assigned to the document.isPowerCordDamaged (one of my booleans), which renders true or false depending on if its checked.
Code:
function Rounding() {
const [cartsRounded, setCartsRounded] = useState([]);
let navigate = useNavigate();
useEffect(() => {
userCartsRounded()
.then((response) => {
setCartsRounded(response.data);
})
.catch((err) => {
console.log(err);
});
}, []);
const columns = [
{
field: "serialNum",
headerName: "Cart Serial Number",
width: 250,
},
{
field: "pcNum",
headerName: "Workstation Number",
width: 250,
},
{
field: "dateLastRounded",
headerName: "Last Rounded On",
width: 250,
},
{
field: "repairsNeeded",
headerName: "Repairs?",
width: 100,
},
{
field: "quarter",
headerName: "Quarter",
width: 75,
},
];
const [sortModel, setSortModel] = React.useState([
{
field: "dateLastRounded",
sort: "desc",
},
]);
const rows = useMemo(
() =>
cartsRounded.map((row, index) => ({
...row,
id: index,
serialNum: row.cartSerialNumber,
pcNum: row.pcNumber,
dateLastRounded: moment(row.updatedAt).format("MM-D-YYYY"),
repairsNeeded: row.isPowerCordDamaged,
quarter: moment(row.updatedAt).format("Qo"),
})),
[cartsRounded]
);
return (
<div>
<IconButton
color="primary"
aria-label="new rounding"
component="span"
onClick={() => {
navigate("add_new_cart");
}}
>
<AddCircleOutline />
</IconButton>
<div style={{ height: 400, width: "100%" }}>
<DataGrid
component={Paper}
rows={rows}
columns={columns}
sortModel={sortModel}
pageSize={100}
rowsPerPageOptions={[100]}
/>
</div>
</div>
);
}
export default Rounding;
Document Example:
{
_id: new ObjectId("61b95e447aec51d938e856cc"),
cartSerialNumber: 'testytitit',
pcNumber: '14124f0sdf0sfs',
isPowerCordDamaged: false,
isFuseBlown: false,
isInverterBad: false,
isInterfaceDamaged: false,
isPhysicalDamage: false,
otherNotes: '',
roundedBy: '6186c13beb18d33d5088f7b2',
createdAt: 2021-12-15T03:17:24.495Z,
updatedAt: 2021-12-15T03:17:24.495Z,
__v: 0
}
I may not be understanding the question here. But I think what you are trying to do is have the repairsNeeded field have a Y or N rather than its boolean value.
Try this.
const rows = useMemo(
() =>
cartsRounded.map((row, index) => ({
...row,
id: index,
serialNum: row.cartSerialNumber,
pcNum: row.pcNumber,
dateLastRounded: moment(row.updatedAt).format("MM-D-YYYY"),
repairsNeeded: row.isPowerCordDamaged ? "Y" : "N" // short hand for if (row.isPowerCordDamaged === true) return "Y" else return "N",
quarter: moment(row.updatedAt).format("Qo"),
})),
[cartsRounded]
);
After looking it may be you want to return Y or N if any are true? In that case best pull it into a function.
const isRepairNeeded = (row) => {
if (row.isPowerCordDamaged) return "Y";
if (row.isFuseBlown) return "Y";
... rest of the ifs
return "N"; // N is only returned if none of the if statements === true;
}
const rows = useMemo(
() =>
cartsRounded.map((row, index) => ({
...row,
id: index,
serialNum: row.cartSerialNumber,
pcNum: row.pcNumber,
dateLastRounded: moment(row.updatedAt).format("MM-D-YYYY"),
repairsNeeded: isRepairNeeded(row) // call the function will return "Y" or "N",
quarter: moment(row.updatedAt).format("Qo"),
})),
[cartsRounded]
);

React ag grid cellRendererFramework click and pass row data but state variable is Undefined

I try to add delete button to my grid row, So I use cellRendererFramework and every thing is fine until state variable return undefined in my root component
I set my state variable with useEffect and every things seems to be OK and my grid shows data correctly
let [roles, SetRoles] = React.useState<Role[]>();
React.useEffect(() => {
List().then((roles) => {
console.log("set roles count:" + roles.data.length);
SetRoles(roles.data);
});
return () => {
console.error("Component will unmount!");
};
}, []);
ColDefs defination:
const columnDefs = [
{
headerName: "کلید",
field: "id",
flex: 0,
hide: true,
},
{
headerName: "عنوان",
field: "title",
flex: 2,
sortable: true,
sort: "asc",
},
{
headerName: "توضیحات جدید",
field: "description",
flex: 6,
},
{
headerName: "حذف",
field: "Id",
flex: 1,
cellRendererFramework: function (row: any) {
return (
<Button
danger
onClick={() => {
onDeleteClick(row.data);
}}
>
حذف
</Button>
);
},
},
];
and my onDeleteHandler and my PROBLEM is
const onDeleteClick = (data: any) => {
//this line works correctly and Id of my row pass to my event
console.log(data.id);
//Issue is here, my loaded state variable in Undefined, But my other function like count of roles work correctly
console.log(roles);
};
<Button
danger
onClick={() => {
onDeleteClick(row.data['id']);
}}
>
حذف
</Button>

How to pass data from state in redux to a react hook state

In my react/redux app, i have data already in the state found in redux and i want to pass that data to my react useState in order to serve the component with the data in terms of getting the current state data or updating the state data.
I have tried but my way is not working. What is the most efficient way of going about solving this issue with react hook. Also how to configure the react useEffect properly.
// action
import { HrEmployeeData } from "../../actions/employeeHR";
const EmployeeTable = ({ employeesDetail, HrEmployeeData }) => {
const [employeesDetail, setEmployeesDetail] = React.useState([]);
useEffect(() => {
HrEmployeeData();
}, []);
const classes = useStyles();
const columns = [
{ name: "name", label: "Name" },
{ name: "phone_no", label: "Contact" },
{ name: "email", label: "Email" },
{ name: "department", label: "Department" },
{ name: "job_title", label: "Title" },
{ name: "salary", label: "Salary" },
{ name: "date_employed", label: "Date Employed" },
{
name: "Action",
options: {
filter: true,
sort: false,
empty: true,
customBodyRender: (value, tableMeta, updateValue) => {
return (
<button
onClick={() =>
window.alert(`Clicked "Edit" for row ${tableMeta.rowIndex}`)
}
>
Edit
</button>
);
},
},
},
];
return (
<div className={classes.root}>
<Grid container spacing={3}>
<Grid item xs={12}>
<Paper className={classes.paper}>
<MUIDataTable
title={"Employees Records"}
data={employeesDetail} <--- WHERE THE DATA IS BEEING USED
columns={columns}
options={options}
/>
</Paper>
</Grid>
</Grid>
</div>
);
};
EmployeeTable.propTypes = {
employeesDetail: PropTypes.object,
};
const mapStateToProps = (state) => ({
employeesDetail: state.employeeHR.employeesDetail,
});
export default connect(mapStateToProps, { HrEmployeeData })(EmployeeTable);
You don't have to pass the data from the redux store the state.
The mechanism to use the redux store in components is "props".
In this case you're already mapping store to props and you can use it in the components.
Just remove the useState line.

Redux state change does not rerenders datatable

**Here is the cart imports and state
const Cart = ({ cart, setActiveTab, removeFromCart, updateQuantity }) => {
const [selectedRows , setSelectedRows] = useState([]);
const [toggleCleared, setToggleCleared] = useState(false);
const [products, setProducts] = useState(cart.products) }
const columns = [
{
name: 'Product Name',
selector: 'name',
sortable: true,
},
{
name: 'Product Price',
selector: 'price',
sortable: true,
right: true,
},
{
name: 'Product Quantity',
selector: 'quantity',
sortable: true,
right: true,
cell: row => <Fragment><input type="number" className="form-control" value={row.quantity} onChange = {(e)=>updateQuantity( row.id,strong text e.target.value)}/></Fragment>
},
{
name: 'Subtotal',
selector: 'quantity',
sortable: true,
right: true,
cell: row => <Fragment>{row.quantity * row.price}</Fragment>
},
{
name: 'Actions',
selector: 'quantity',
sortable: true,
right: true,
cell: row => <Fragment><i className="fa fa-trash" aria-hidden="true" onClick = {(e)=>removeFromCart(row.id)}></i></Fragment>
},
];
const contextActions = useMemo(() => {
const handleDelete = () => {
selectedRows.forEach(item=>{
console.log(item, 'item')
removeFromCart(products, products.indexOf(item))
});
setProducts(products);
setToggleCleared(!toggleCleared);
console.log('handleDelete', products)
};
return <i className="fa fa-trash" key="delete" onClick={handleDelete} style={{ backgroundColor: 'red' }} ></i>;
});
This code sets the selected rows when we select rows
function rowSelection (rows) =>{
setSelectedRows(rows.selectedRows);
}
return ( <DataTable
title="Cart Products"
columns={columns}
data={products}
selectableRows
onSelectedRowsChange={rowSelection}
clearSelectedRows={toggleCleared}
contextActions={contextActions} />
)
**here is the my cart component which works fine but it does not updates itself when the redux state changes.. i can see the redux state in console that it changes perfectly but datatable does not rerenders
const [products, setProducts] = useState(cart.products) } this will only works on initial rendering ( called initial state).useState will not call once the render is completed. so just pass the cart.products in to the datatable
function rowSelection (rows) =>{
setSelectedRows(rows.selectedRows);
}
return ( <DataTable
title="Cart Products"
columns={columns}
data={cart.products}
selectableRows
onSelectedRowsChange={rowSelection}
clearSelectedRows={toggleCleared}
contextActions={contextActions} /> )

Resources