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);
CodePudding user response:
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>
CodePudding user response:
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>
);
})
CodePudding user response:
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>
);