I get following error / warning during rendering:
Warning: Each child in a list should have a unique "key" prop.
Check the render method of `App`. See .. for more information.
in ListItemCustom (at App.js:137)
in App (created by WithStyles(App))
in WithStyles(App) (at src/index.js:7)
What to do? Do I need to add a uniq key to my ListItem
material-ui
component?
App.js:
import React, { Component } from "react";
import AppBar from "@material-ui/core/AppBar";
import Toolbar from "@material-ui/core/Toolbar";
import Typography from "@material-ui/core/Typography";
import Button from "@material-ui/core/Button";
import FacebookLogin from "react-facebook-login";
import Menu from "@material-ui/core/Menu";
import MenuItem from "@material-ui/core/MenuItem";
import List from "@material-ui/core/List";
import ListItem from "@material-ui/core/ListItem";
import ListItemText from "@material-ui/core/ListItemText";
import ArrowForwardIos from "@material-ui/icons/ArrowForwardIos";
import ArrowBackIos from "@material-ui/icons/ArrowBackIos";
import axios from "axios";
import ListItemCustom from "./components/ListItemCustom";
import ListSubheader from "@material-ui/core/ListSubheader";
import Switch from "@material-ui/core/Switch";
import TextField from "@material-ui/core/TextField";
import Box from "@material-ui/core/Box";
import IconButton from "@material-ui/core/IconButton";
// import this
import { withStyles } from "@material-ui/core/styles";
// make this
const styles = theme => ({
root: {
flexGrow: 1
},
menuButton: {
marginRight: theme.spacing(2)
},
title: {
flexGrow: 1
},
listSubHeaderRoot: {
backgroundColor: "#E5E5E5",
color: "#252525",
lineHeight: "22px"
}
});
class App extends Component {
state = {
accessToken: "",
isLoggedIn: false,
userID: "",
name: "",
email: "",
picture: "",
selectedEvent: undefined,
buyOrRelease: "buy",
pages: []
};
responseFacebook = response => {
this.setState({
accessToken: response.accessToken,
isLoggedIn: true,
userID: response.userID,
name: response.name,
email: response.email,
picture: response.picture.data.url
});
let accessToken = response.accessToken;
axios
.get(
"https://graph.facebook.com/v5.0/me/accounts?fields=id,name&access_token=" +
response.accessToken
)
.then(async pagesResponse => {
let promisesArray = pagesResponse.data.data.map(async page => {
console.log("page " + page.id + " " + page.name);
return axios
.get(
"https://graph.facebook.com/v5.0/" +
page.id +
"/events?fields=id,name&access_token=" +
accessToken
)
.catch(e => e);
});
const responses = await Promise.all(promisesArray);
var pages = [];
responses.forEach((response, i) => {
const page = pagesResponse.data.data[i];
pages.push({
id: page.id,
name: page.name,
events: response.data.data
});
});
this.setState({
pages: pages
});
});
};
handleClick = event =>
this.setState({
anchorEl: event.currentTarget
});
handleClose = () => {
this.setState({ anchorEl: undefined });
};
handleCloseAndLogOut = () => {
this.setState({ anchorEl: undefined });
this.setState({ isLoggedIn: undefined });
this.setState({ userID: undefined });
this.setState({ name: undefined });
this.setState({ email: undefined });
this.setState({ picture: undefined });
};
switchToRelease = () => {
this.setState({ buyOrRelease: "release" });
};
switchToBuy = () => {
this.setState({ buyOrRelease: "buy" });
};
componentDidMount() {
document.title = "Tiket.hu";
}
handleSort = event => {
this.setState({ selectedEvent: event });
};
navigateBack = () => {
this.setState({ selectedEvent: undefined });
};
render() {
let fbOrMenuContent;
let listContent;
let buyOrReleaseMenuItem;
if (this.state.isLoggedIn) {
let eventsList;
if (this.state.buyOrRelease === "buy") {
} else {
eventsList = this.state.pages.map(page => {
let eventsList2 = page.events.map(event => (
<ListItemCustom key={event.id} value={event} onHeaderClick={this.handleSort} />
));
return (
<div>
<ListSubheader className={this.props.classes.listSubHeaderRoot} key={page.id}>{page.name}</ListSubheader>
{eventsList2}
</div>
);
});
}
listContent = (
<div>
<List component="nav" aria-label="main mailbox folders">
{eventsList}
</List>
</div>
);
if (this.state.selectedEvent) {
listContent = (
<div>
<List component="nav" aria-label="main mailbox folders">
<ListItem button onClick={this.navigateBack}>
<IconButton edge="start" aria-label="delete">
<ArrowBackIos />
</IconButton>
<Box textAlign="left" style={{ width: 150 }}>
Back
</Box>
<ListItemText
secondaryTypographyProps={{ align: "center" }}
primary={this.state.selectedEvent.name}
/>
</ListItem>
<ListItem button>
<Box textAlign="left" style={{ width: 150 }}>
Select auditorium
</Box>
<ListItemText
secondaryTypographyProps={{ align: "right" }}
secondary="UP Újpesti Rendezvénytér"
/>
<IconButton edge="end" aria-label="delete">
<ArrowForwardIos />
</IconButton>
</ListItem>
<ListItem button>
<Box textAlign="left" style={{ width: 150 }}>
Release purpose
</Box>
<ListItemText
secondaryTypographyProps={{ align: "right" }}
secondary="Normal selling"
/>
<IconButton edge="end" aria-label="delete">
<ArrowForwardIos />
</IconButton>
</ListItem>
<ListItem>
<ListItemText primary="Start selling" />
<Switch edge="end" />
</ListItem>
<ListItem>
<ListItemText primary="Notify if different price would increase revenue" />
<Switch edge="end" />
</ListItem>
<ListSubheader className={this.props.classes.listSubHeaderRoot}>
Sector
</ListSubheader>
<ListItem button>
<Box textAlign="left" style={{ width: 150 }}>
Select sector
</Box>
<ListItemText
secondaryTypographyProps={{ align: "right" }}
secondary="A"
/>
<IconButton edge="end" aria-label="delete">
<ArrowForwardIos />
</IconButton>
</ListItem>
<ListItem button>
<Box textAlign="left" style={{ width: 500 }}>
Marketing resource configuration & result
</Box>
<ListItemText
secondaryTypographyProps={{ align: "right" }}
secondary=""
/>
<IconButton edge="end" aria-label="delete">
<ArrowForwardIos />
</IconButton>
</ListItem>
<ListItem>
<ListItemText primary="Price in sector" />
<TextField InputLabelProps={{ shrink: true }} />
</ListItem>
</List>
</div>
);
}
if (this.state.buyOrRelease === "buy") {
buyOrReleaseMenuItem = (
<Menu
id="simple-menu"
anchorEl={this.state.anchorEl}
keepMounted
open={Boolean(this.state.anchorEl)}
onClose={this.handleClose}
>
<MenuItem onClick={this.handleCloseAndLogOut}>Log out</MenuItem>
<MenuItem onClick={this.switchToRelease}>
Switch Release mode
</MenuItem>
<MenuItem onClick={this.handleClose}>My tickets</MenuItem>
</Menu>
);
} else {
buyOrReleaseMenuItem = (
<Menu
id="simple-menu"
anchorEl={this.state.anchorEl}
keepMounted
open={Boolean(this.state.anchorEl)}
onClose={this.handleClose}
>
<MenuItem onClick={this.handleCloseAndLogOut}>Log out</MenuItem>
<MenuItem onClick={this.switchToBuy}>Switch Buy mode</MenuItem>
</Menu>
);
}
fbOrMenuContent = (
<div>
<Button
aria-controls="simple-menu"
aria-haspopup="true"
onClick={this.handleClick}
>
{this.state.name}
</Button>
{buyOrReleaseMenuItem}
</div>
);
} else {
let fbAppId;
if (
window.location.hostname === "localhost" ||
window.location.hostname === "127.0.0.1"
)
fbAppId = "402670860613108";
else fbAppId = "2526636684068727";
fbOrMenuContent = (
<FacebookLogin
appId={fbAppId}
autoLoad={true}
fields="name,email,picture"
scope="public_profile,pages_show_list"
onClick={this.componentClicked}
callback={this.responseFacebook}
/>
);
}
return (
<div className="App">
<AppBar position="static">
<Toolbar>
<Typography variant="h6" className={this.props.classes.title}>
Tiket.hu
</Typography>
<Button color="inherit">Search</Button>
<Button color="inherit">Basket</Button>
{fbOrMenuContent}
</Toolbar>
</AppBar>
{listContent}
</div>
);
}
}
export default withStyles(styles)(App);
ListItemCustom.js:
import React, { Component } from "react";
import ListItem from "@material-ui/core/ListItem";
import ListItemIcon from "@material-ui/core/ListItemIcon";
import ListItemText from "@material-ui/core/ListItemText";
import ArrowForwardIos from "@material-ui/icons/ArrowForwardIos";
export default class ListItemCustom extends Component {
eventSelected = () => {
this.props.onHeaderClick(this.props.value);
};
render() {
return (
<ListItem button key={this.props.value.id} onClick={this.eventSelected}>
<ListItemText primary={this.props.value.name}/>
<ListItemIcon>
<ArrowForwardIos />
</ListItemIcon>
</ListItem>
);
}
}
The Solution. When creating a list in the UI from an array with JSX, you should add a key prop to each child and to any of its' children. React uses the key prop create a relationship between the component and the DOM element.
⚠️ Warning: Each child in a list should have a unique “key” prop. This is because React uses a unique “key” prop on each child of the list to create a relationship between the component and the DOM. This is to ensure that react re-renders the child correctly next time.
Unstable keys can crash the whole app. Finally, we should note that keys need only be unique among adjacent elements or sibling list items. A key does not have to be globally unique, but only within the scope of a list.
Keys help React identify which items have changed, are added, or are removed. Keys should be given to the elements inside the array to give the elements a stable identity: const numbers = [1, 2, 3, 4, 5]; const listItems = numbers.
You should add an unique prop to your components inside .map
that is inside your render
eventsList = this.state.pages.map(page => {
let eventsList2 = page.events.map((event, i) => (
// unique key prop
<ListItemCustom key={i} value={event} onHeaderClick={this.handleSort} />
));
return (
<div key={page.name}> // unique key prop
<ListSubheader>{page.name}</ListSubheader>
{eventsList2}
</div>
);
});
Please notice that using i
(the index) isn't good, you should have an unique property like an id
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With