在ReactJ中映射时,如何用OnClick在列表中的单个项目?

发布于 2025-02-04 23:53:09 字数 5083 浏览 5 评论 0原文

我的React组件从我的Firestore DB返回数据,并将其在材料-UI卡上映射。但是,当我按ExpandMoreicon时,它会打开每张卡。我只想单独打开每张卡。我知道该解决方案与扩展的Usestate功能有关。设定。

我试图修复此错误,但我似乎无法使其起作用。任何帮助将不胜感激。

export const NFTprojects = () => {

const [expanded, setExpanded] = useState(false);

const handleExpandClick = (id) => {
    setExpanded(!expanded)
};

const [projects, setProjects] = useState([]);

const ref = firebase.firestore().collection("NFTprojects");

function getProjects() {
    ref.onSnapshot((querySnapshot) => {
        const items = []; //initiliaze empty array
        querySnapshot.forEach((doc) => {
            items.push(doc.data());
        });
        setProjects(items);
    });
}

useEffect(() => {
    getProjects();
}, []);


return (
    <div>
        <Grid container spacing={4} direction="row" justifyContent="flex-start" alignItems="flex-start">

            {projects.map((project) => (
                <Grid item xs={4}>
                    <Card sx={{ maxWidth: 400, borderRadius: 3, mb: 5 }}>
                        <CardMedia
                            component="img"
                            height="140"
                            image={project.imageUrl}
                            alt={project.projectName}
                        />
                        <CardContent>
                            <Typography gutterBottom variant="h5" sx={{ fontWeight: 'bold' }}>
                                {project.projectName}
                            </Typography>
                            <Typography variant="h6" gutterBottom component="div" fontWeight="bold">
                                {project.jobPosition}
                            </Typography>
                            <Typography variant="body2" color="text.secondary" style={{ fontFamily: 'Merriweather' }}>
                                {project.projectDesc}
                            </Typography>
                        </CardContent>
                        <CardActions disableSpacing>

                            <Tooltip title="Website">
                                <IconButton aria-label="secondary marketplace" href={project.websiteLink} target="_blank">
                                    <WebsiteIcon />
                                </IconButton>
                            </Tooltip>

                            <Tooltip title="Twitter">
                                <IconButton aria-label="twitter" href={project.twitterLink} target="_blank">
                                    <TwitterIcon />
                                </IconButton>
                            </Tooltip>

                            <Tooltip title="Secondary">
                                <IconButton aria-label="Secondary market link" href={project.secondaryMarket} target="_blank">
                                    <ShoppingCartIcon />
                                </IconButton>
                            </Tooltip>

                            <Tooltip title="Discord">
                                <IconButton aria-label="discord" href={project.discordLink} target="_blank">
                                    <SvgIcon component={DiscordIcon} viewBox="0 0 600 476.6" />
                                </IconButton>
                            </Tooltip>


                            <Button size="small" variant="contained" sx={{ ml: 15, backgroundColor: 'black' }}>Apply</Button>


                            <ExpandMore
                                expand={expanded}
                                onClick={handleExpandClick}
                                aria-expanded={expanded}
                                aria-label="show more"
                            >
                                <ExpandMoreIcon />
                            </ExpandMore>

                        </CardActions>

                        <Collapse in={expanded} timeout="auto" unmountOnExit>
                            <CardContent>
                                <Typography variant="h6" sx={{ fontWeight: 'bold' }} style={{ fontFamily: 'Merriweather' }}>Job Description:</Typography>
                                <Typography paragraph>
                                    {project.jobDesc}
                                </Typography>

                                <Typography variant="h6" sx={{ fontWeight: 'bold' }}>Prerequisites</Typography>
                                <Typography paragraph>
                                    {project.jobPrereq}
                                </Typography>

                            </CardContent>
                        </Collapse>

                    </Card>
                </Grid>
            ))}
        </Grid>
    </div >
);

}

My react component returns data from my Firestore DB and maps the data it on Material-UI cards. However, when I press the ExpandMoreIcon, it opens EVERY card. I just want to open each card individually. I know the solution has to do with useState function for expanded & setExpanded.

I've tried to fix this bug but I cant seem to make it work. Any help would be greatly appreciated.

export const NFTprojects = () => {

const [expanded, setExpanded] = useState(false);

const handleExpandClick = (id) => {
    setExpanded(!expanded)
};

const [projects, setProjects] = useState([]);

const ref = firebase.firestore().collection("NFTprojects");

function getProjects() {
    ref.onSnapshot((querySnapshot) => {
        const items = []; //initiliaze empty array
        querySnapshot.forEach((doc) => {
            items.push(doc.data());
        });
        setProjects(items);
    });
}

useEffect(() => {
    getProjects();
}, []);


return (
    <div>
        <Grid container spacing={4} direction="row" justifyContent="flex-start" alignItems="flex-start">

            {projects.map((project) => (
                <Grid item xs={4}>
                    <Card sx={{ maxWidth: 400, borderRadius: 3, mb: 5 }}>
                        <CardMedia
                            component="img"
                            height="140"
                            image={project.imageUrl}
                            alt={project.projectName}
                        />
                        <CardContent>
                            <Typography gutterBottom variant="h5" sx={{ fontWeight: 'bold' }}>
                                {project.projectName}
                            </Typography>
                            <Typography variant="h6" gutterBottom component="div" fontWeight="bold">
                                {project.jobPosition}
                            </Typography>
                            <Typography variant="body2" color="text.secondary" style={{ fontFamily: 'Merriweather' }}>
                                {project.projectDesc}
                            </Typography>
                        </CardContent>
                        <CardActions disableSpacing>

                            <Tooltip title="Website">
                                <IconButton aria-label="secondary marketplace" href={project.websiteLink} target="_blank">
                                    <WebsiteIcon />
                                </IconButton>
                            </Tooltip>

                            <Tooltip title="Twitter">
                                <IconButton aria-label="twitter" href={project.twitterLink} target="_blank">
                                    <TwitterIcon />
                                </IconButton>
                            </Tooltip>

                            <Tooltip title="Secondary">
                                <IconButton aria-label="Secondary market link" href={project.secondaryMarket} target="_blank">
                                    <ShoppingCartIcon />
                                </IconButton>
                            </Tooltip>

                            <Tooltip title="Discord">
                                <IconButton aria-label="discord" href={project.discordLink} target="_blank">
                                    <SvgIcon component={DiscordIcon} viewBox="0 0 600 476.6" />
                                </IconButton>
                            </Tooltip>


                            <Button size="small" variant="contained" sx={{ ml: 15, backgroundColor: 'black' }}>Apply</Button>


                            <ExpandMore
                                expand={expanded}
                                onClick={handleExpandClick}
                                aria-expanded={expanded}
                                aria-label="show more"
                            >
                                <ExpandMoreIcon />
                            </ExpandMore>

                        </CardActions>

                        <Collapse in={expanded} timeout="auto" unmountOnExit>
                            <CardContent>
                                <Typography variant="h6" sx={{ fontWeight: 'bold' }} style={{ fontFamily: 'Merriweather' }}>Job Description:</Typography>
                                <Typography paragraph>
                                    {project.jobDesc}
                                </Typography>

                                <Typography variant="h6" sx={{ fontWeight: 'bold' }}>Prerequisites</Typography>
                                <Typography paragraph>
                                    {project.jobPrereq}
                                </Typography>

                            </CardContent>
                        </Collapse>

                    </Card>
                </Grid>
            ))}
        </Grid>
    </div >
);

}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

海未深 2025-02-11 23:53:09

一种方法是为卡创建单独的组件。这将使您能够将状态添加到组件并控制它们。这是一个最小的示例,展示了如何处理它。

import React, { useState } from "react";

// this is just sample data to work with - equivalent to the data you get from Firebase
const sampleCardsArray = [
  {
    id: 0,
    name: "Card 1",
    color: "red",
    description: "This is card 1",
  },
  {
    id: 1,
    name: "Card 2",
    color: "blue",
    description: "This is card 2",
  },
  {
    id: 2,
    name: "Card 3",
    color: "green",
    description: "This is card 3",
  },
];

// component for all cards
export const AllCards = () => {
    // this state is used to store the INDEX of the card that is currently expanded
  const [expandedCard, setExpandedCard] = useState(null);
  return (
    <div>
      {sampleCardsArray.map((card, index) => (
        <OneCard
          card={card}
          key={card.id}
          // this prop passes the boolean value of whether the card is expanded or not
          isExpanded={expandedCard === index}
          // this prop receives the index of the card that is expanded and sets the state
          expandCard={() => setExpandedCard(index)}
        />
      ))}
    </div>
  );
};

// component for one card
// We only show the fields: name and color. We show the description when the card is clicked
export const OneCard = ({ card, isExpanded, expandCard }) => {
  return (
    <div>
      <h1>{card.name}</h1>
      <h2>{card.color}</h2>

      {
        // showing expand button only when card is not expanded
      }
      {isExpanded ? (
        <p>{card.description}</p>
      ) : (
        <button onClick={() => expandCard()}>Expand card</button>
      )}
    </div>
  );
};

One approach is to create a separate component for the card. This will enable you to add states to the component and control them. Here is a minimal example demonstrating how you can approach it.

import React, { useState } from "react";

// this is just sample data to work with - equivalent to the data you get from Firebase
const sampleCardsArray = [
  {
    id: 0,
    name: "Card 1",
    color: "red",
    description: "This is card 1",
  },
  {
    id: 1,
    name: "Card 2",
    color: "blue",
    description: "This is card 2",
  },
  {
    id: 2,
    name: "Card 3",
    color: "green",
    description: "This is card 3",
  },
];

// component for all cards
export const AllCards = () => {
    // this state is used to store the INDEX of the card that is currently expanded
  const [expandedCard, setExpandedCard] = useState(null);
  return (
    <div>
      {sampleCardsArray.map((card, index) => (
        <OneCard
          card={card}
          key={card.id}
          // this prop passes the boolean value of whether the card is expanded or not
          isExpanded={expandedCard === index}
          // this prop receives the index of the card that is expanded and sets the state
          expandCard={() => setExpandedCard(index)}
        />
      ))}
    </div>
  );
};

// component for one card
// We only show the fields: name and color. We show the description when the card is clicked
export const OneCard = ({ card, isExpanded, expandCard }) => {
  return (
    <div>
      <h1>{card.name}</h1>
      <h2>{card.color}</h2>

      {
        // showing expand button only when card is not expanded
      }
      {isExpanded ? (
        <p>{card.description}</p>
      ) : (
        <button onClick={() => expandCard()}>Expand card</button>
      )}
    </div>
  );
};

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文