You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React Hooks+Material-UI实现单张卡片独立展开的问题求助

解决单张卡片独立展开的问题

嘿,我完全明白你遇到的困扰——用单一布尔值控制所有卡片的展开状态,肯定会导致一点击就全展开/收起的问题。咱们只需要调整状态的存储方式,就能让每张卡片独立控制自己的展开状态了,具体步骤如下:

1. 重构expanded状态

把原来的布尔值状态改成记录当前展开卡片的索引,初始值设为null表示没有卡片展开:

// 替换原来的 useState(false)
const [expanded, setExpanded] = React.useState(null);

2. 更新点击事件处理函数

修改handleExpandClick,让它根据点击的索引来判断是展开还是收起当前卡片:

function handleExpandClick(index) {
  // 如果当前点击的卡片已经展开,就收起(设为null);否则展开该卡片
  setExpanded(prevExpanded => prevExpanded === index ? null : index);
}

3. 调整组件的展开判断逻辑

现在每个卡片需要判断自己的索引是否和expanded状态一致,来决定是否展开:

  • 对于IconButton的样式和aria-expanded属性:
    <IconButton
      className={clsx(classes.expand, {
        [classes.expandOpen]: expanded === index // 只对当前索引的卡片应用展开样式
      })}
      onClick={() => handleExpandClick(index)}
      aria-expanded={expanded === index} // 只标记当前索引的卡片为展开状态
      aria-label="show more"
    >
      <ExpandMoreIcon />
    </IconButton>
    
  • 对于Collapse组件的in属性:
    <Collapse in={expanded === index} timeout="auto" unmountOnExit>
      {/* 展开内容 */}
    </Collapse>
    

修改后的完整MediaCard组件关键代码

const useStyles = makeStyles(theme => ({
  card: { width: 200, marginBottom: 16 },
  media: { height: 0, paddingTop: "56.25%" },
  expand: {
    transform: "rotate(0deg)",
    marginLeft: "auto",
    transition: theme.transitions.create("transform", { duration: theme.transitions.duration.shortest })
  },
  expandOpen: { transform: "rotate(180deg)" },
  avatar: { backgroundColor: red[500], margin: 0 },
  card_container: {
    display: "flex",
    maxWidth: 1064,
    flexWrap: "wrap",
    margin: "auto",
    justifyContent: "space-between"
  }
}));

const MediaCard = (props) => {
  const classes = useStyles();
  const [expanded, setExpanded] = React.useState(null);

  function handleExpandClick(index) {
    setExpanded(prevExpanded => prevExpanded === index ? null : index);
  }

  const cardList = props.data.map((data, index) => {
    return (
      <Card className={classes.card} key={index}>
        <div style={{ display: "flex", flexDirection: "column", justifyContent: "center", alignItems: "center", marginTop: 16 }} >
          <Avatar aria-label="recipe" className={classes.avatar}>
            {data.name.charAt(0)}
          </Avatar>
          <Typography variant="h5" style={{ margin: "8px auto" }}>
            {data.name}
          </Typography>
        </div>
        <IconButton
          className={clsx(classes.expand, {
            [classes.expandOpen]: expanded === index
          })}
          onClick={() => handleExpandClick(index)}
          aria-expanded={expanded === index}
          aria-label="show more"
        >
          <ExpandMoreIcon />
        </IconButton>
        <Collapse in={expanded === index} timeout="auto" unmountOnExit>
          <CardContent>
            <Typography paragraph>{data.gender}</Typography>
            <Typography paragraph>{data.eye_color}</Typography>
            <Typography paragraph>{data.height}</Typography>
          </CardContent>
        </Collapse>
      </Card>
    );
  });

  return <div className={classes.card_container}>{cardList}</div>;
};

export default MediaCard;

为什么这样能解决问题?

现在我们的状态expanded存储的是当前展开卡片的索引,而不是一个全局的布尔值。每张卡片只会检查自己的索引是否和状态中的值匹配,只有匹配的卡片才会切换展开状态,这样就实现了单张卡片的独立控制啦!

内容的提问来源于stack exchange,提问作者Kabeh2

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.14 06:24:37