Using the same component for a few different pages in my project, but on one specific page called “customersPage” it returns
Warning: Each child in a list should have a unique “key” prop.
Check the render method ofDetailedRow
. See https://reactjs.org/link/warning-keys for more information.
at div
at DetailedRow (http://localhost:3001/static/js/bundle.js:1816:5)
at tbody
at http://localhost:3001/static/js/bundle.js:4740:66
at TableBody (http://localhost:3001/static/js/bundle.js:26111:82)
at table
at http://localhost:3001/static/js/bundle.js:4740:66
at Table (http://localhost:3001/static/js/bundle.js:26979:82)
at div
at http://localhost:3001/static/js/bundle.js:4740:66
at Paper (http://localhost:3001/static/js/bundle.js:23349:83)
at http://localhost:3001/static/js/bundle.js:4740:66
at TableContainer (http://localhost:3001/static/js/bundle.js:26511:82)
at div
at TableComp (http://localhost:3001/static/js/bundle.js:2090:62)
at div
at CustomersPage (http://localhost:3001/static/js/bundle.js:505:77)
I’ve tried adding a unique key prop to the main div elements and the components but with no changes. Keep in mind that I use the same component on different pages and all are passing the same props, just with different values.
Here’s customersPage:
const CustomersPage = () => {
const storeData = useSelector(state => state);
const [open, setOpen] = React.useState(false);
const [success, setSuccess] = React.useState(false);
React.useEffect(() => {
customerData();
}, [storeData.purchases]);
const customerData = () => {
console.log(storeData.customers);
const customers = storeData.customers.map(customer => {
const purchases = storeData.purchases.filter(purchase=> purchase.customerId === customer.id);
const customerWithPurchases = {
...customer,
purchases: purchases.map(purchase => {
const product = storeData.products.find(product => product.id === purchase.productId);
return {
...purchase,
product: product
};
})
};
return customerWithPurchases});
return customers;
}
const [data, setData] = React.useState(customerData);
const handleDialog = () => {setOpen(!open);}
React.useEffect(() => {
if (success) {
const timeoutId = setTimeout(() => {
setSuccess(false);
}, 3000);
return () => {
clearTimeout(timeoutId);
}
}
}, [success]);
return (
<div key={data} style={{backgroundColor: '#0288d1', height: '600px'}}>
<BuyProductDialog open={open} setSuccess={setSuccess} handleDialog={handleDialog}></BuyProductDialog>
<br/><br/>
<ColorButton variant="contained" size="large" onClick={handleDialog}>Buy Product</ColorButton>
{success? (<Alert severity="success">This is a success alert — check it out!</Alert>) : ('')}
<br/><br/>
<TableComp context={'customers'} key={data} data={data}/>
</div>
)
}
const ColorButton = styled(Button)(({ theme }) => ({
color: theme.palette.getContrastText(purple[500]),
backgroundColor: purple[500],
'&:hover': {
backgroundColor: purple[700],
},
}));
export default CustomersPage;
TableComp.js:
export const TableComp = (props) => {
const [rows, setRows] = React.useState([]);
const [columns, setColumns] = React.useState([])
const [data, setData] = React.useState(props.data);
const storeData = useSelector(state => state);
React.useEffect(() => {
if (props.context === 'products') {
setRows(storeData.products);
setColumns(['Name', 'Price', 'Quantity']);
}
else if (props.context === 'customers')
{
setRows(data);
setColumns(['Name','Purchased Products','Purchase Dates'])
}
else if (props.context === 'purchases') {
const purchases = storeData.purchases.filter(purchase => purchase.productId === props.product.id);
const customerIds = purchases.map(purchase => {return {id: purchase.customerId, date: purchase.date}});
const customers = customerIds.map(customerId => {
const customer = storeData.customers.find(customer => customer.id === customerId.id);
return {...customer, date: customerId.date};
});
setRows(customers);
setColumns(['Name', 'City', 'Date'])
}
else if (props.context === 'allPurchases') {
setRows(data);
setColumns(['Name', 'Product', 'Date'])
}
else if (props.context === 'editCustomers')
{
setRows(data.purchases);
setColumns(['Name','Purchased Product','Purchase Date']);
}
}, [props.context]);
return (
<div style={{height: '250px'}} key={rows}>
<TableContainer component={Paper}>
<Table aria-label="collapsible table">
<TableHead>
<TableRow>
<TableCell />
<TableCell>{columns[0]}</TableCell>
<TableCell align="center">{columns[1]}</TableCell>
<TableCell align="right">{columns[2]} </TableCell>
</TableRow>
</TableHead>
<TableBody>
{rows.map((row) => <DetailedRow key={row.id} context={props.context} row={row} />
)}
</TableBody>
</Table>
</TableContainer>
</div>
);
}
export default React.memo(TableComp);
DetailedRow.js:
const DetailedRow = (props) => {
const { row } = props;
const storeData = useSelector(state => state);
const location = useLocation();
const locationContext = location.pathname || '';
const [open, setOpen] = useState(false);
const [cells, setCells] = useState([]);
const [context, setContext] = useState('');
useEffect(() => {checkContext();}, [row, props.context]);
const checkContext = () => {
if (props.context === 'products') {
setCells([row.name, row.price, row.quantity]);
setContext(props.context);
}
else if (props.context === 'purchases') {
setCells([(row.fname + ' ' + row.lname), row.city, row.date]);
setContext('customers');
}
else if (props.context === 'customers') {
const products = row.purchases.map(purchase => purchase.product);
const purchaseDates = row.purchases.map(purchase => purchase.date);
setCells([(row.fname + ' ' + row.lname), products ,purchaseDates]);
setContext('customers')
}
else if (props.context === 'allPurchases')
{
setCells([row.customerName, row.productName, row.date]);
setContext('purchases');
}
else if (props.context === 'editCustomers')
{const customer = storeData.customers.find(customer => customer.id === row.customerId)
setCells([(customer.fname +" "+customer.lname), row.productName, row.purchaseDate])
setContext('editCustomers')}
}
return (
<React.Fragment key={row.id}>
<TableRow sx={{ '& > *': { borderBottom: 'unset' } }}>
<TableCell>
<IconButton aria-label="expand row"
size="small" onClick={() => setOpen(!open)}>
{props.context === 'products'? (open ? <KeyboardArrowUpIcon /> : <KeyboardArrowDownIcon />) : ''}
</IconButton>
</TableCell>
<TableCell component="th" scope="row">
{context === 'customers' ? (<Link key={row.id} to={`/customers/${row.id}`}>{cells[0]}</Link>) :
(context === 'purchases'? (<Link to={`/customers/${row.customerId}`}>{cells[0]}</Link>) :
(context === 'editCustomers'? (cells[0]) :
(<Link to={`/${context}/${row.id}`}>{cells[0]}</Link>)))}
</TableCell>
<TableCell align="center">
{context === 'customers' ? ( locationContext.includes('/products/')? (cells[1]) :
(cells[1].map(product => {
const productId = row.purchases.find(purchase => purchase.product.name === product.name)?.product.id;
return (<div key={row.id}><Link to={`/products/${productId}`} key={productId}>{product.name}</Link>, </div>);}))) :
(context === 'purchases'? (<Link to={`/products/${row.productId}`}>{cells[1]}</Link>) :
(context === 'editCustomers'? <Link to={`/products/${row.productId}`}>{cells[1]}</Link>:cells[1]))}
</TableCell>
<TableCell align="right">
{context === 'editCustomers'? (cells[2]) : (cells[2] && locationContext.includes('/customer')?
(cells[2].map(date => {const tempDate = date.toString();
return(<div>{tempDate}, </div>)}))
: (cells[2]))}
</TableCell>
</TableRow>
<TableRow>
<TableCell style={{ paddingBottom: 0, paddingTop: 0 }} colSpan={6}>
<Collapse in={open} timeout="auto" unmountOnExit>
<PurchasedComp key={row.id} row={row} context={props.context}/>
</Collapse>
</TableCell>
</TableRow>
</React.Fragment>
);
}
export default React.memo(DetailedRow);