I have a list users, those details want to show in table format. Want to check if map has value, if not show the message 'no data found'.
how can I achieve that.?
{filteredList && filteredList
.slice(page * rowsPerPage, page * rowsPerPage + rowsPerPage)
.map(row =>
( row===null ? (
<TableRow key={row.id}>
<TableCell className={classes.tableCell}>{items++}</TableCell>
<TableCell className={classes.tableCell}>
{row.sp_Name}
</TableCell>
<TableCell className={classes.tableCell}>{row.sp_Phone}</TableCell>
<TableCell className={classes.tableCell}>{row.sp_Role}</TableCell>
<TableCell className={classes.tableCell}>{row.sp_Service}</TableCell>
<TableCell className={classes.tableCell}>{row.sp_Location}</TableCell>
<TableCell className={classes.tableCell}>
<Link to={'/admin/profile/' + row.id} key={row.id} style={{ textDecoration: "none" }} >
<Chip
icon={<FaceIcon />}
label="View Profile/Action"
color="primary"
className={classes.chip}
variant="outlined"
onClcik={this.handleAction}
/>
</Link>
</TableCell>
<TableCell className={classes.tableCell}>
<Tooltip title="Delete">
<DeleteIcon color="danger" onClick={() => this.handleClickDialogOpen(row.id)} className={classes.icon} />
{/* <DeleteIcon onClick={() => deleteSP(row.id)} className={classes.icon} /> */}
</Tooltip>
</TableCell>
</TableRow>
) : "no data"
)
)}
Your can check if data exist first, if yes, render the table and row. If no data,
then you just render the required 'no data found' text
getData = () => {
const filteredList = this.state.list // your logic to get data
// Perform your logic to filter the data
return filteredList.slice(page * rowsPerPage, page * rowsPerPage + rowsPerPage)
}
render() {
const filteredList = this.getData()
return (
filteredList.length > 0 ?
<table>
{data.map( /* Make your <tr> */ )}
</table> :
"no data found"
)
}
Related
I have a table with items. I use cookies to save them. Also, I have two buttons (increase and decrease). When I press them to change qty it works, but it also puts the item's row to the bottom of the list. I need to keep them in the same position where they are. I used sortDirection prop to TableCell component to make an order, but it also didn't work. Please help update the code so I can keep items in the same position.
Thanks in advance.
export default function CartItemsTable() {
const [cookies, setCookie, removeCookie] = useCookies();
function IncreaseQTY(article) {
var newCookie = cookies[article];
newCookie.qty++;
setCookie(article, newCookie, {
expires: new Date(Date.now() + 604800000),
});
}
function DecreaseQTY(article) {
var newCookie = cookies[article];
newCookie.qty--;
if (newCookie.qty === 0) {
removeCookie(article);
} else {
setCookie(article, newCookie, {
expires: new Date(Date.now() + 604800000),
});
}
}
return (
<TableContainer component={Paper}>
<Table sx={{ minWidth: 650 }} >
<TableHead>
<TableRow>
<TableCell >Name</TableCell>
<TableCell sortDirection="asc" align="center">Code</TableCell>
<TableCell align="center">Price</TableCell>
<TableCell align="center">QTY</TableCell>
<TableCell align="center">Total</TableCell>
</TableRow>
</TableHead>
<TableBody>
{Object.keys(cookies).map(function (key, index) {
return (
<TableRow
key={index}
sx={{ "&:last-child td, &:last-child th": { border: 0 } }}
>
<TableCell component="th" scope="row">
{cookies[key].name}
</TableCell>
<TableCell align="center">{cookies[key].article}</TableCell>
<TableCell align="center">{cookies[key].price}</TableCell>
<TableCell align="center">
<ButtonGroup
variant="contained"
aria-label="outlined primary button group"
>
<Button
onClick={() => {
DecreaseQTY(cookies[key].article);
}}
>
-
</Button>
<Button variant="text" disableRipple={true}>
{cookies[key].qty}
</Button>
<Button
onClick={() => {
IncreaseQTY(cookies[key].article);
}}
>
+
</Button>
</ButtonGroup>
</TableCell>
<TableCell align="center">
{cookies[key].qty * cookies[key].price}
</TableCell>
</TableRow>
);
})}
</TableBody>
</Table>
</TableContainer>
);
}
Before I press the button
After I press the increase button, first line went to the bottom
Update
I guess this problem can be occurred because of the cookies order or even related to react-cookie package, so I added console.log(cookies); to IncreaseQTY function. After a couple increasing it starts to show this way
const ResultsAccordion = ({projects, projectStatus}) =>
{
return (
<Accordion>
<AccordionSummary expandIcon={<ExpandMoreIcon />} aria-controls="panel1a-content" id="panel1a-header">
<Typography>{projectStatus}</Typography>
</AccordionSummary>
<AccordionDetails>
<TableContainer component={Paper} sx={{my:2 }}>
<Table>
<TableHead>
<TableRow style={{ width:"100%" }}>
<TableCell align="center">Team Name</TableCell>
<TableCell align="center">Project Link</TableCell>
<TableCell align="center">Team Members</TableCell>
</TableRow>
</TableHead>
<TableBody>
{
projects.map((value) =>
{
return (
<TableRow key={value.name}>
<TableCell align="center">
{value.name}
</TableCell>
<TableCell align="center">
<Link href={value.repo}>Project Repo</Link>
</TableCell>
<TableCell align="center">
<Stack direction="row" spacing={1} alignContent="center">
{
(value.members).map((item, key) =>
{
return (
<Chip key={key} component="a" href={`https://github.com/${item.github}`} clickable label={item.github} avatar={<Avatar alt={item.github} src={item.avatar}/> } />
);
})
}
</Stack>
</TableCell>
</TableRow>
);
})
}
</TableBody>
</Table>
</TableContainer>
</AccordionDetails>
</Accordion>
);
};
Chips are supposed to be returned in the Table Cell with Stack..
But there is no one.
I used the react Developers tools and there is no childrens in Stack.
The chips are rendered if i made any changes in the prop using ReactDevTools
I checked, the value.members array & it is also not empty.
I am doing a project in order to run a contracting company and somewhere I have to show all the receipts for each user of the company, but I got this error:
Warning: Each child in a list should have a unique "key" prop.
Although I looked at the code, I didn't find any errors
How can I solve the problem?
Through this file, I display a table, and this table displays a list of receipts
import FuseScrollbars from "#fuse/core/FuseScrollbars";
import _ from "#lodash";
import Checkbox from "#material-ui/core/Checkbox";
import Icon from "#material-ui/core/Icon";
import Table from "#material-ui/core/Table";
import TableBody from "#material-ui/core/TableBody";
import TableCell from "#material-ui/core/TableCell";
import TablePagination from "#material-ui/core/TablePagination";
import TableRow from "#material-ui/core/TableRow";
import Typography from "#material-ui/core/Typography";
import clsx from "clsx";
import { motion } from "framer-motion";
import { useEffect, useState } from "react";
import { useDispatch, useSelector } from "react-redux";
import { withRouter } from "react-router-dom";
import FuseLoading from "#fuse/core/FuseLoading";
import {
getSalaryScales,
selectSalaryScales,
} from "../store/salaryScalesSlice";
import SalaryScalesTableHead from "./SalaryScalesTableHead";
import Moment from "react-moment";
import IconButton from "#material-ui/core/IconButton";
import KeyboardArrowDownIcon from "#material-ui/icons/KeyboardArrowDown";
import KeyboardArrowUpIcon from "#material-ui/icons/KeyboardArrowUp";
function SalaryScalesTable(props) {
const dispatch = useDispatch();
const salaryScales = useSelector(selectSalaryScales);
const searchText = useSelector(
({ salaryScalesApp }) => salaryScalesApp.salaryScales.searchText
);
const [loading, setLoading] = useState(true);
const [selected, setSelected] = useState([]);
const [data, setData] = useState(salaryScales);
const [page, setPage] = useState(0);
const [rowsPerPage, setRowsPerPage] = useState(10);
const [order, setOrder] = useState({
direction: "asc",
id: null,
});
const [open, setOpen] = useState(false);
console.log("order: ", order);
useEffect(() => {
dispatch(getSalaryScales()).then(() => setLoading(false));
}, [dispatch]);
useEffect(() => {
if (searchText.length !== 0) {
setData(
_.filter(salaryScales, (item) =>
item.id.toLowerCase().includes(searchText.toLowerCase())
)
);
setPage(0);
} else {
setData(salaryScales);
}
}, [salaryScales, searchText]);
function handleRequestSort(event, property) {
const id = property;
let direction = "desc";
if (order.id === property && order.direction === "desc") {
direction = "asc";
}
setOrder({
direction,
id,
});
}
function handleSelectAllClick(event) {
if (event.target.checked) {
setSelected(data.map((n) => n.id));
return;
}
setSelected([]);
}
function handleDeselect() {
setSelected([]);
}
function handleClick(item) {
props.history.push(`/apps/salary-scales-section/salary-scales/${item.id}`);
}
function handleCheck(event, id) {
const selectedIndex = selected.indexOf(id);
let newSelected = [];
if (selectedIndex === -1) {
newSelected = newSelected.concat(selected, id);
} else if (selectedIndex === 0) {
newSelected = newSelected.concat(selected.slice(1));
} else if (selectedIndex === selected.length - 1) {
newSelected = newSelected.concat(selected.slice(0, -1));
} else if (selectedIndex > 0) {
newSelected = newSelected.concat(
selected.slice(0, selectedIndex),
selected.slice(selectedIndex + 1)
);
}
setSelected(newSelected);
}
function handleChangePage(event, value) {
setPage(value);
}
function handleChangeRowsPerPage(event) {
setRowsPerPage(event.target.value);
}
if (loading) {
return <FuseLoading />;
}
if (data.length === 0) {
return (
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 1, transition: { delay: 0.1 } }}
className="flex flex-1 items-center justify-center h-full"
>
<Typography color="textSecondary" variant="h5">
There are no Salary Scales!
</Typography>
</motion.div>
);
}
return (
<div className="w-full flex flex-col">
<FuseScrollbars className="flex-grow overflow-x-auto">
<Table stickyHeader className="min-w-xl" aria-label="collapsible table">
<SalaryScalesTableHead
selectedSalaryScaleIds={selected}
order={order}
onSelectAllClick={handleSelectAllClick}
onRequestSort={handleRequestSort}
rowCount={data.length}
onMenuItemClick={handleDeselect}
/>
<TableBody>
{_.orderBy(
data,
[
(o) => {
switch (order.id) {
case "categories": {
return o.categories[0];
}
default: {
return o[order.id];
}
}
},
],
[order.direction]
)
.slice(page * rowsPerPage, page * rowsPerPage + rowsPerPage)
.map((n) => {
const isSelected = selected.indexOf(n.id) !== -1;
return (
<>
<TableRow
className="h-72 cursor-pointer"
hover
role="checkbox"
aria-checked={isSelected}
tabIndex={-1}
key={n.id}
selected={isSelected}
onClick={(event) => handleClick(n)}
>
<TableCell
className="w-40 md:w-64 text-center"
padding="none"
>
<Checkbox
checked={isSelected}
onClick={(event) => event.stopPropagation()}
onChange={(event) => handleCheck(event, n.id)}
/>
</TableCell>
<TableCell>
<IconButton
aria-label="expand row"
size="small"
onClick={() => setOpen(!open)}
className="w-40 md:w-64 text-center"
padding="none"
>
{open ? (
<KeyboardArrowUpIcon />
) : (
<KeyboardArrowDownIcon />
)}
</IconButton>
</TableCell>
<TableCell
className="p-4 md:p-16"
component="th"
scope="row"
align="left"
>
{n.id}
</TableCell>
<TableCell
className="p-4 md:p-16"
component="th"
scope="row"
align="center"
>
<Moment>{n.createdAt}</Moment>
</TableCell>
<TableCell
className="p-4 md:p-16"
component="th"
scope="row"
align="center"
>
{n.isActive ? (
<Icon className="text-green text-20">
check_circle
</Icon>
) : (
<Icon className="text-red text-20">
remove_circle
</Icon>
)}
</TableCell>
</TableRow>
</>
);
})}
</TableBody>
</Table>
</FuseScrollbars>
<TablePagination
className="flex-shrink-0 border-t-1"
component="div"
count={data.length}
rowsPerPage={rowsPerPage}
page={page}
backIconButtonProps={{
"aria-label": "Previous Page",
}}
nextIconButtonProps={{
"aria-label": "Next Page",
}}
onPageChange={handleChangePage}
onRowsPerPageChange={handleChangeRowsPerPage}
/>
</div>
);
}
export default withRouter(SalaryScalesTable);
Your problem is from <><TableRow key={n.id}></TableRow></>
In your case, I think you don't need to have <></>, so you can get rid of that, your key missing problem will be solved
return (
<TableRow
className="h-72 cursor-pointer"
hover
role="checkbox"
aria-checked={isSelected}
tabIndex={-1}
key={n.id}
selected={isSelected}
onClick={(event) => handleClick(n)}
>
<TableCell
className="w-40 md:w-64 text-center"
padding="none"
>
<Checkbox
checked={isSelected}
onClick={(event) => event.stopPropagation()}
onChange={(event) => handleCheck(event, n.id)}
/>
</TableCell>
<TableCell>
<IconButton
aria-label="expand row"
size="small"
onClick={() => setOpen(!open)}
className="w-40 md:w-64 text-center"
padding="none"
>
{open ? (
<KeyboardArrowUpIcon />
) : (
<KeyboardArrowDownIcon />
)}
</IconButton>
</TableCell>
<TableCell
className="p-4 md:p-16"
component="th"
scope="row"
align="left"
>
{n.id}
</TableCell>
<TableCell
className="p-4 md:p-16"
component="th"
scope="row"
align="center"
>
<Moment>{n.createdAt}</Moment>
</TableCell>
<TableCell
className="p-4 md:p-16"
component="th"
scope="row"
align="center"
>
{n.isActive ? (
<Icon className="text-green text-20">
check_circle
</Icon>
) : (
<Icon className="text-red text-20">
remove_circle
</Icon>
)}
</TableCell>
</TableRow>
If you want to maintain <></> (as the shortcut for <React.Fragment></React.Fragment>). You need to explicitly call
<React.Fragment key={n.id}>
<TableRow></TableRow>
</React.Fragment>
List must have a unique key while mapping. The best way to pick a key is to use a string that uniquely identifies a list item among its siblings. Most often you would use IDs from your data as keys.
YourArray
.slice(page * rowsPerPage, page * rowsPerPage + rowsPerPage)
.map((n) => {
const isSelected = selected.indexOf(n.id) !== -1;
return (
<React.Fragment key={n.id}>
<TableRow
className="h-72 cursor-pointer"
hover
role="checkbox"
aria-checked={isSelected}
tabIndex={-1}
key={n.id}
selected={isSelected}
onClick={(event) => handleClick(n)}
>
<TableCell
className="w-40 md:w-64 text-center"
padding="none"
>
<Checkbox
checked={isSelected}
onClick={(event) => event.stopPropagation()}
onChange={(event) => handleCheck(event, n.id)}
/>
</TableCell>
<TableCell>
<IconButton
aria-label="expand row"
size="small"
onClick={() => setOpen(!open)}
className="w-40 md:w-64 text-center"
padding="none"
>
{open ? (
<KeyboardArrowUpIcon />
) : (
<KeyboardArrowDownIcon />
)}
</IconButton>
</TableCell>
<TableCell
className="p-4 md:p-16"
component="th"
scope="row"
align="left"
>
{n.id}
</TableCell>
<TableCell
className="p-4 md:p-16"
component="th"
scope="row"
align="center"
>
<Moment>{n.createdAt}</Moment>
</TableCell>
<TableCell
className="p-4 md:p-16"
component="th"
scope="row"
align="center"
>
{n.isActive ? (
<Icon className="text-green text-20">
check_circle
</Icon>
) : (
<Icon className="text-red text-20">
remove_circle
</Icon>
)}
</TableCell>
</TableRow>
</React.Fragment>
);
})
Here you must give key to first div:
return (
<div key={n.id}>
<TableRow
className="h-72 cursor-pointer"
hover
role="checkbox"
aria-checked={isSelected}
tabIndex={-1}
selected={isSelected}
onClick={(event) => handleClick(n)}
>
<TableCell
className="w-40 md:w-64 text-center"
padding="none"
>
<Checkbox
checked={isSelected}
onClick={(event) => event.stopPropagation()}
onChange={(event) => handleCheck(event, n.id)}
/>
</TableCell>
<TableCell>
<IconButton
aria-label="expand row"
size="small"
onClick={() => setOpen(!open)}
className="w-40 md:w-64 text-center"
padding="none"
>
{open ? (
<KeyboardArrowUpIcon />
) : (
<KeyboardArrowDownIcon />
)}
</IconButton>
</TableCell>
<TableCell
className="p-4 md:p-16"
component="th"
scope="row"
align="left"
>
{n.id}
</TableCell>
<TableCell
className="p-4 md:p-16"
component="th"
scope="row"
align="center"
>
<Moment>{n.createdAt}</Moment>
</TableCell>
<TableCell
className="p-4 md:p-16"
component="th"
scope="row"
align="center"
>
{n.isActive ? (
<Icon className="text-green text-20">
check_circle
</Icon>
) : (
<Icon className="text-red text-20">
remove_circle
</Icon>
)}
</TableCell>
</TableRow>
</div>
);
I have a very peculiar case to display Selected Item Count in my Material UI - Accordion Summary:
Following Should be the Behavior:
When a User click on the Header Checkbox it should select All the Item in that Panel and then shows the Count of those Items as X Selected
Right Now I am able to do the following :
As You can see the Number of Items selected is showing on Each Panel But I only Want it to show in the Selected Panel.
Below is my Code for Above screen Shot:
{unsubscribedEmployeeData ? Object.entries(unsubscribedEmployeeData).map(([name, value]) => {
return (
<Accordion TransitionProps={{ unmountOnExit: true }}>
<AccordionSummary
expandIcon={<ExpandMoreIcon />}
aria-label="Expand"
aria-controls="additional-actions1-content"
id="additional-actions1-header"
>
<EnhancedTableToolbar numSelected={selected.length} name={name} values={value} />
</AccordionSummary>
<AccordionDetails>
<Typography color="textSecondary">
<div className={classes.root}>
<Paper className={classes.paper}>
<TableContainer>
<Table
className={classes.table}
aria-labelledby="tableTitle"
size={dense ? 'small' : 'medium'}
aria-label="enhanced table"
>
<TableBody>
{stableSort(value, getComparator(order, orderBy))
.slice(page * rowsPerPage, page * rowsPerPage + rowsPerPage)
.map((row, index) => {
const isItemSelected = isSelectedID(row.id);
const labelId = `enhanced-table-checkbox-${index}`;
return (
<TableRow
hover
onClick={(event) => handleClick(event, row.first_name + " " + row.last_name, row.id)}
role="checkbox"
aria-checked={isItemSelected}
tabIndex={-1}
key={row.id}
selected={isItemSelected}
>
<TableCell padding="checkbox">
<Checkbox
checked={isItemSelected}
inputProps={{ 'aria-labelledby': labelId }}
/>
</TableCell>
<TableCell align="center" component="th" id={labelId} scope="row" >
{row.first_name + " " + row.last_name}
</TableCell>
<TableCell align="center">{row.email}</TableCell>
</TableRow>
);
})}
</TableBody>
</Table>
</TableContainer>
</Paper>
</div>
</Typography>
</AccordionDetails>
</Accordion>
);
}) : undefined}
EnhancedTableToolbar
const EnhancedTableToolbar = (props) => {
const classes = useToolbarStyles();
const { numSelected, name, values } = props;
return (
<Toolbar
className={clsx(classes.root)}
>
{numSelected > 0 ? (
<>
<EnhancedTableHead
classes={classes}
numSelected={selected.length}
onSelectAllClick={(event) => handleSelectAllClick(event,values)}
rowCount={values.length}
label={name}
/>
<Typography component="div">
{numSelected} selected
</Typography>
</>
) : (
<EnhancedTableHead
classes={classes}
numSelected={selected.length}
onSelectAllClick={(event) => handleSelectAllClick(event,values)}
rowCount={values.length}
label={name}
/>
)}
</Toolbar>
);
};
handleSelectAllClick
const handleSelectAllClick = (event, val) => {
if (event.target.checked) {
const newSelectedsID = val.map((n) => n.id);
const newSelecteds = val.map((n) => n.first_name + " " + n.last_name);
console.log(newSelecteds);
console.log(newSelectedsID);
setSelectedID(newSelectedsID);
setSelected(newSelecteds);
return;
}
setSelected([]);
setSelectedID([]);
};
Please guide me through this. Thanks
The function is called on form submission. at first, it is working as expected but when calling this function on form submit. I get this following error.index.js:1 Warning: React has detected a change in the order of Hooks called by null. This will lead to bugs and errors if not fixed. For more information, read the Rules of Hooks:
Previous render Next render
useState useState
undefined useState
Here is the code sample :
function TablerowsAdd ( x,i,header,handleRemove, startEditing,editIdx,handleChange,stopEditing,handleRemoveField,fieldIdx,startEditingField,fieldheader) {
const [open, setOpen] = useState(false);
const currentlyEditing = editIdx === i;
return (
<React.Fragment key={`tr-${i}`}>
<TableRow key={`tr-${i}`}>
{header.map((y, k) => (
<TableCell key={`trc-${k}`}>
{currentlyEditing ? (
<TextField
name={y.prop}
onChange={e => handleChange(e, y.prop, i)}
value={x[y.prop]}
/>
) : (
x[y.prop]
)}
</TableCell>
))}
<TableCell>
{currentlyEditing ? (
<CheckIcon onClick={() => stopEditing()} />
) : (
<EditIcon onClick={() => startEditing(i)} />
)}
<DeleteIcon onClick={() => handleRemove(i)} />
<IconButton aria-label="expand row" size="small" onClick={() => setOpen(!open)} >
{open ? <KeyboardArrowUpIcon /> : <KeyboardArrowDownIcon />}
</IconButton>
</TableCell>
</TableRow>
<TableRow>
<TableCell style={{ paddingBottom: 0, paddingTop: 0 }} colSpan={6}>
<Collapse in={open} timeout="auto" unmountOnExit>
<Box margin={1}>
<Typography variant="h6" gutterBottom component="div">
Fields
</Typography>
<Table size="small" aria-label="purchases">
<TableHead>
<TableRow>
{fieldheader.map((x, i) => (
<TableCell key={`thc-${i}`}>{x.name} </TableCell>
))}
</TableRow>
</TableHead>
<TableBody>
{x.fields.map((fieldRow,y) => (
<TableRow key={`tr-${y}`}>
<TableCell>{fieldRow.bitname}</TableCell>
<TableCell>{fieldRow.bitmask}</TableCell>
<TableCell >{fieldRow.bitvalue}</TableCell>
<TableCell >{fieldRow.maskname}</TableCell>
<TableCell > {fieldRow.doc} </TableCell>
<TableCell >
<DeleteIcon onClick={() => handleRemoveField(i,fieldRow.bitname,y)} /> </TableCell>
</TableRow>
))}
</TableBody>
</Table>
</Box>
</Collapse>
</TableCell>
</TableRow>
</React.Fragment>
); };
If call count of TablerowsAdd varies, the call count of useState varies too.
Try using a component: <TablerowsAdd/> instead of method call: TablerowsAdd(). This way useState is called exactly once per component