'Issue with javascript map function

I am working with the map function in react and I am getting all my info to map except for my button ref. Below is the code. The data is getting pulled from a database and passed as a prop as well. When I filter the list I get the look I am looking for but when I go to select a doctor I only get the last name for the doctor that was the last item iterated, and this is across all the buttons when I click them. Where am I going wrong if I may ask.

import {
  Button,
  Card,
  CardActions,
  CardContent,
  Grid,
  Typography,
} from "@material-ui/core";
import { makeStyles } from "@material-ui/styles";
import { useRef } from "react";

const styles = makeStyles({
  bound: {
    margin: "40px 200px",
    position: "relative",
  },
  card: {
    maxWidth: "70%",
    margin: "50px auto",
    borderStyle: "solid",
    borderRadius: "50px",
    borderColor: "black",
    borderWidth: "3px",
    "&:hover": {
      boxShadow: "black",
      borderColor: "green",
    },
  },
  null: {
    display: "none",
  },
  btn: {
    margin: "20px 0 0 0",
    borderRadius: "20px",
    "&:hover": {
      backgroundColor: "green",
      color: "white",
    },
  },
});

const SearchResult = (props) => {
  const doctorList = props.doctor;
  const selectedRef = useRef();

  const selectedHandler = (e) => {
    console.log(e.target.target);
    console.log(selectedRef.current.value);
  };
  const doctorCard = (doctorList) => {
    return (
      <Card
        variant="outlined"
        className={`${doctorList ? classes.card : classes.null}`}
        key={doctorList.user_id}
        id={doctorList.user_id}
      >
        <CardContent>
          <Grid container>
            <Grid xs={6} md={6} lg={6} item>
              <img src="#" alt="Img.png" />
            </Grid>
            <Grid xs={6} md={6} lg={6} item>
              <Typography variant="h6">
                Dr. {doctorList.first_name} {doctorList.last_name}
              </Typography>
              <Typography variant="h6">
                Speciality: {doctorList.title}
              </Typography>
              <Typography variant="h6">
                Primary Language: {doctorList.primary_language}
              </Typography>
            </Grid>
          </Grid>
          <CardActions>
            <Button
              variant="contained"
              className={classes.btn}
              type="submit"
              ref={selectedRef}
              value={doctorList.last_name}
              onClick={selectedHandler}
              size="large"
              fullWidth
            >
              Select
            </Button>
          </CardActions>
        </CardContent>
      </Card>
    );
  };

  const classes = styles();
  return (
    <div className={classes.bound}>{doctorList.doctor.map(doctorCard)}</div>
  );
};

export default SearchResult;

OUTPUT enter image description here



Solution 1:[1]

You are looping list and using common reference 'selectedRef' in you list component. Once list rendering finishes, last_name of last item in your array is being stored in 'selectedRef' reference. That's why whenever you click it's always getting last item.

To resolve this you might want to break component on granular level. Use two component ItemList and ListItem. ItemList will simply loop array and in ListItem component create reference to each item while looping. That should resolve your issue.

const ItemList = (props) => {
  const renderList=(doctor)=>{
     return <ListItem key={`doctor-${doctor.id}`} {...doctor}/>
  }
  return <div>{props.data.map(renderList)}</div>
}

const ListItem = () => {
  // reference below create new reference for every item
  const selectedRef = useRef();
  // rest of logic to render doctor card
}

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Manish Mahajan