Fix " unique 'key' prop" error while using React fragments

Viewed 1386

I keep getting "Warning: Each child in a list should have a unique 'key' prop." error in the console. After some checking, I found that a key needs to be supplied for to each child as well as each element inside children. I have added keys to the Grid, Link and Card component in the map function but the error persists. Do the children of the Card component need keys too? Because I don't think I have enough unique keys. Or am I adding keys to the wrong components? Please help.

export default function CourseCards() {
  const classes = useStyles();
  const [anchorEl, setAnchorEl] = React.useState(null);

  const handlePopoverOpen = (event) => {
    setAnchorEl(event.currentTarget);
  };

  const handlePopoverClose = () => {
    setAnchorEl(null);
  };

  const open = Boolean(anchorEl);

  // COURSE CARDS DATA
  const courseData = [
    {
      title: "Data Science  >",
      description: "Machine Learning bootcamp",
      price: "$236",
      rating: "",
      url: "/",
      img:
        "https://uploads.codesandbox.io/uploads/user/1/skny-12.jpg"
    },
    {
      title: "ApacheBlaBla >",
      description: "Coding and Basics",
      price: "",
      rating: "",
      url: "/",
      img:
        "https://uploads.codesandbox.io/uploads/user/2/e-7V-11.jpg"
    },
    {
      title: "temporary",
      description: "Testing",
      price: "",
      rating: "",
      url: "/",
      img:
        "https://uploads.codesandbox.io/uploads/user/42/Nmwx-16.jpg"
    },
    {
      title: "",
      description: "Great",
      price: "Free",
      rating: "",
      url: "/",
      img:
        "https://uploads.codesandbox.io/uploads/user/12/iiVp15.jpg"
    },
    {
      title: "Java >",
      description: "Engine Creating for PS4",
      price: "$20",
      rating: "",
      url: "/",
      img:
        "https://uploads.codesandbox.io/uploads/user/2/S0J2-13.jpg"
    },
    {
      title: "App Development >",
      description: " Developments Basics",
      price: "$30",
      rating: "",
      url: "/",
      img:
        "https://uploads.codesandbox.io/uploads/user/142/gV14.jpg"
    }
  ];

//MAPPING FUNCTION
  const courseCards = courseData.map((course, index) => (
    <>
      <Grid key={course.description} item xs={12} sm={4}>
        <Link key={course.title+index} to={course.url} className={classes.link}>
          <Card
            key={course.img}
            style={{
              maxWidth: 345,
              height: "100%",
              paddingBottom: 32,
              borderRadius: 0
            }}
          >
            <CardActionArea>
              <CardMedia
                style={{ height: 140 }}
                image={course.img}
                title={course.title}
              />
              <CardHeader
                action={
                  <IconButton
                    aria-label="more"
                    aria-owns={open ? "mouse-over-popover" : undefined}
                    aria-haspopup="true"
                    onMouseEnter={handlePopoverOpen}
                    onMouseLeave={handlePopoverClose}
                  >
                    <MoreVertIcon />
                  </IconButton>
                }
              />
              <CardContent>
                <Typography
                  gutterBottom
                  variant="body2"
                  color="textSecondary"
                  component="p"
                >
                  {course.title}
                </Typography>
                <Typography variant="h6" component="h2">
                  {course.description}
                </Typography>
              </CardContent>
            </CardActionArea>
            <CardActions
              style={{
                display: "flex",
                flexDirection: "row",
                justifyContent: "space-between"
              }}
            >
              <Button size="small" color="primary">
                {course.rating}
              </Button>
              <Button size="small" color="primary">
                {course.price}
              </Button>
            </CardActions>
          </Card>
        </Link>
      </Grid>

      <Popover
        id="mouse-over-popover"
        className={classes.popover}
        classes={{
          paper: classes.paper
        }}
        open={open}
        anchorEl={anchorEl}
        anchorOrigin={{
          vertical: "center",
          horizontal: "center"
        }}
        transformOrigin={{
          vertical: "center",
          horizontal: "right"
        }}
        onClose={handlePopoverClose}
        disableRestoreFocus
      >
        {/* POPOVER CARDS */}
        <CoursePopoverCards title={course.title} />
      </Popover>
    </>
  ));

  return (
    <div>
      <Container className={classes.container}>
        <Grid
          container
          direction="row"
          justify="space-around"
          alignItems="stretch"
          spacing={3}
        >
          {courseCards}
        </Grid>
        <Grid
          container
          direction="row"
          justify="center"
          className={classes.showBtnBox}
        >
          <Button
            variant="contained"
            className={classes.showBtn}
            color="primary"
            size="large"
          >
            Show All
          </Button>
        </Grid>
      </Container>
    </div>
  );
}
3 Answers

You should give the key to the Fragment element as it contains everything else and is the topmost node for each element of the array. You can remove the keys in the child elements.

This <></> syntax doesn't support keys/attributes so you should use the Fragment element this way:

const courseCards = courseData.map((course, index) => (
    <React.Fragment key={//some unique value}>
      <Grid item xs={12} sm={4}>
      ...
    </React.Fragment>

Not for each Element in the child(list), you should add key to root element of the list. Just wrap list element with Fragment add key to it.

const courseCards = courseData.map((course, index) => (
    <React.Fragemnt key={course.title+index}>
      <Grid item xs={12} sm={4}>
        <Link to={course.url} className={classes.link}>
          <Card
            key={course.img}
            style={{
              maxWidth: 345,
              height: "100%",
              paddingBottom: 32,
              borderRadius: 0
            }}
          >
           // Stuff
          </Card>
        </Link>
      </Grid>

      <Popover
        id="mouse-over-popover"
        className={classes.popover}
        classes={{
          paper: classes.paper
        }}
        open={open}
        anchorEl={anchorEl}
        anchorOrigin={{
          vertical: "center",
          horizontal: "center"
        }}
        transformOrigin={{
          vertical: "center",
          horizontal: "right"
        }}
        onClose={handlePopoverClose}
        disableRestoreFocus
      >
        {/* POPOVER CARDS */}
        <CoursePopoverCards title={course.title} />
      </Popover>
    </React.Fragment>
  ));

Provided key in map should be unique, do this way

<Grid key={i} item xs={12} sm={4}>
  ...
</Grid>
Related