If Statement Inside Map - reactjs

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]
);

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;

MUI datagrid view mode not persisting

I'm implementing a MUI datagrid with inline editing. Whenever a cell loses focus, the code sets the mode to 'view' (as it should) but then immediately the processRowUpdate callback is called (to send data to the endpoint) it sets the cell mode back to 'edit' meaning that the cell remains in edit mode.
Does anyone know why this is happening?
Maybe something to do with this processRowUpdate error logged to console?:
TypeError: Cannot read properties of undefined (reading 'id')
at getRowId (productTable.js:213:1)
at getRowIdFromRowModel (gridRowsUtils.js:17:1)
at useGridRows.js:106:1
at Array.forEach (<anonymous>)
at Object.updateRows (useGridRows.js:105:1)
at apiRef.current.<computed> [as updateRows] (useGridApiMethod.js:14:1)
at useGridCellEditing.new.js:334:1
Code:
export default function FullFeaturedCrudGrid(props) {
const [rows, setRows] = React.useState([]);
const [cellModesModel, setCellModesModel] = React.useState({})
const [selectedCellParams, setSelectedCellParams] = React.useState(null);
const { tableName } = props
const [snackbar, setSnackbar] = React.useState(null);
const handleCloseSnackbar = () => setSnackbar(null);
React.useEffect(() => {
console.log('useEffect called')
axios.get(`http://localhost:8000/mvhr/all`)
.then((response) => {
setRows(response.data);
})
}, [])
React.useEffect(() => {
console.log('cellModesModel',cellModesModel)
});
const handleCellFocus = React.useCallback((event) => {
const row = event.currentTarget.parentElement;
const id = row.dataset.id;
const field = event.currentTarget.dataset.field;
setSelectedCellParams({ id, field });
}, []);
const handleDeleteClick = (id) => () => {
axios.delete(`http://localhost:8000/delete_mvhr/${id}`
).then(() => {
setRows(rows.filter((row) => row.id !== id));
setSnackbar({ children: tableName + ' successfully deleted', severity: 'success' });
})
};
const handleCancel = () => {
if (!selectedCellParams) {
return;
}
const { id, field } = selectedCellParams;
setCellModesModel({
...cellModesModel,
[id]: {
...cellModesModel[id],
[field]: { mode: GridCellModes.View },
},
});
};
const processRowUpdate = React.useCallback(
(newRow) => {
axios.put(`http://localhost:8000/mvhr/`, newRow)
.then((response) => {
const updatedRow = { ...newRow, isNew: false };
setRows(rows.map((row) => (row.id === newRow.id ? updatedRow : row)));
setSnackbar({ children: tableName + ' successfully saved', severity: 'success' });
return updatedRow
})
});
const handleProcessRowUpdateError = React.useCallback((error) => {
setSnackbar({ children: error.message, severity: 'error' });
}, []);
const columns = [
{ field: 'description', headerName: 'description', width: 180, editable: true },
{ field: 'elec_efficiency', headerName: 'elec_efficiency', type: 'number', editable: true },
{ field: 'heat_recovery_eff', headerName: 'heat_recovery_eff', type: 'number', editable: true },
{ field: 'range_low', headerName: 'range_low', type: 'number', editable: true },
{ field: 'range_high', headerName: 'range_high', type: 'number', editable: true },
{ field: 'superseded', headerName: 'superseded', type: 'boolean', editable: true },
{
field: 'actions',
type: 'actions',
headerName: 'Actions',
width: 100,
cellClassName: 'actions',
getActions: ({ id }) => {
return [
<GridActionsCellItem
icon={<DeleteIcon />}
label="Delete"
onClick={handleDeleteClick(id)}
color="inherit"
/>,
];
},
},
];
return (
<Box
sx={{
height: '100vh',
width: '100%',
'& .actions': {
color: 'text.secondary',
},
'& .textPrimary': {
color: 'text.primary',
},
}}
>
<StripedDataGrid
rows={rows}
columns={columns}
processRowUpdate={processRowUpdate}
onProcessRowUpdateError={handleProcessRowUpdateError}
onCellEditStop={handleCancel}
cellModesModel={cellModesModel}
onCellModesModelChange={(model) => setCellModesModel(model)}
components={{
Toolbar: AddToolbar,
}}
componentsProps={{
toolbar: { setRows, setSnackbar, tableName },
cell: {
onFocus: handleCellFocus,
},
}}
experimentalFeatures={{ newEditingApi: true }}
getRowClassName={(params) =>
params.indexRelativeToCurrentPage % 2 === 0 ? 'even' : 'odd'
}
/>
{!!snackbar && (
<Snackbar
open
anchorOrigin={{ vertical: 'bottom', horizontal: 'center' }}
onClose={handleCloseSnackbar}
autoHideDuration={6000}
>
<Alert {...snackbar} onClose={handleCloseSnackbar} />
</Snackbar>
)}
</Box>
);
}
Solved it. I refactored the code to return the updatedRow synchronously rather than as a callback to the axios request:
const processRowUpdate = React.useCallback(
(newRow) => {
const updatedRow = { ...newRow, isNew: false };
setRows(rows.map((row) => (row.id === newRow.id ? updatedRow : row)));
setSnackbar({ children: tableName + ' successfully saved', severity: 'success' });
axios.put(`http://localhost:8000/mvhr/`, newRow)
return updatedRow
});

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

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.

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>

react-table keeps re-rendering unchanged cells

I am using react-table plugin, and my cells keep re-rendering while I am changing the applied searching filter even though their values aren't changed.
As seen in the video, name and debt TDs keep updating, even though their values are static.
https://i.imgur.com/2KkNs9f.mp4
I imagine, that may impact performance on larger tables.
Is there any fix? Or am I doing anything wrong?
Thanks.
Edit:
Code has been requested. Not much to show, basically everything done as in documentation.
Rendering part:
render(){
const columns = [
{
Header: "Name",
accessor: "name",
className: css.cell,
headerClassName: css.cell,
filterMethod: (filter, row) => {
return row[filter.id]
.toLowerCase()
.includes(filter.value.toLowerCase());
},
Cell: props => {
return <span>{props.value}</span>;
}
},
{
Header: "Tc",
accessor: d => {
const date = d.lastChange;
if (date.length) {
const s = date.split(" ");
const s2 = s[0].split("/");
const mdy = [s2[1], s2[0], s2[2]].join("/");
const his = s[1];
return Date.parse(mdy + " " + his);
}
return "";
},
id: "lastChange",
Cell: props => {
return <ArrowTime lastChange={props.value}></ArrowTime>;
},
headerClassName: css.cell,
className: css.cell
},
{
Header: "Debt",
accessor: d => Number(d.lastDebt),
id: "lastDebt",
headerClassName: css.cell,
className: css.cell,
Cell: props => {
return <span className="number">{props.value}</span>;
},
getProps: (state, rowInfo, column) => {
return {
style: {
background: rowInfo ? this.getColor(rowInfo.row.lastDebt) : null
}
};
}
}
];
return (
<ReactTable
data={this.props.table}
columns={columns}
minRows={0}
showPagination={false}
NoDataComponent={CustomNoDataComponent}
className={css.table}
resizable={false}
filtered={[{ id: "name", value: this.props.filter }]}
getTrProps={(state, rowInfo) => {
return {
className: rowInfo ? css.subRow : ""
};
}}
getTrGroupProps={(state, rowInfo) => {
return {
className: rowInfo ? css.row : ""
};
}}
getTbodyProps={props => {
return {
className: props ? css.tbody : ""
};
}}
getTheadProps={props => {
return {
className: props ? css.thead : ""
};
}}
defaultSorted={[
{
id: "lastDebt",
desc: true
}
]}
/>
);
}

Resources