React+Material UI中卡片下拉菜单同步展开问题求助
问题原因
所有卡片共用了同一个expanded状态变量,点击任意卡片的展开按钮时,全局状态会被修改,导致所有绑定该状态的卡片同步展开/收起。
解决方案:抽离独立卡片组件
把单张卡片封装成独立组件,让每个组件自己管理展开状态,实现卡片间的独立控制。
优化后的代码如下:
const ExpandMore = styled((props) => { const { expand, ...other } = props; return <IconButton {...other} />; })(({ theme, expand }) => ({ transform: !expand ? 'rotate(0deg)' : 'rotate(180deg)', marginLeft: 'auto', transition: theme.transitions.create('transform', { duration: theme.transitions.duration.shortest, }), })); // 独立卡片组件,自带状态管理 function BlogCard({ title, subheader, imageUrl, previewText, detailContent }) { const [expanded, setExpanded] = React.useState(false); const handleExpandClick = () => { setExpanded(!expanded); }; return ( <Box m={6}> <Card sx={{ maxWidth: 345 }}> <CardHeader title={title} subheader={subheader} /> <CardMedia component="img" height="194" image={imageUrl} alt="Blog cover" /> <CardContent> <Typography variant="body1" color="text.secondary"> {previewText} </Typography> </CardContent> <CardActions disableSpacing> <ExpandMore expand={expanded} onClick={handleExpandClick} aria-expanded={expanded} aria-label="show more" > <ExpandMoreIcon /> </ExpandMore> </CardActions> <Collapse in={expanded} timeout="auto" unmountOnExit> <CardContent> {detailContent} </CardContent> </Collapse> </Card> </Box> ); } function Blogs(){ // 卡片数据统一管理,新增卡片只需添加数据项 const blogData = [ { title: "It’s a cocktail o’clock.", subheader: "May 14, 2021", imageUrl: "https://img.freepik.com/free-photo/selection-various-cocktails-table_140725-2909.jpg?w=2000", previewText: "Beat The Heat With Chilled Cocktails At The Best Bars In New York.", detailContent: ( <> <Typography paragraph>To a foodie, summer means one thing and one thing only – DRINKS!</Typography> <Typography paragraph> Yes people, we know it’s hot out there and the only way to quench your thirst is by guzzling down a bunch of ice-cold cocktails </Typography> <Typography paragraph> <h4>1.The Bar Room at The Beekman</h4> Visit the beautiful Bar Room in the historic Beekman Hotel for high-key romance that really wows. Do try the whiskey sour.One of the best drinks available. <h4>2.Dublin House</h4> You can never go wrong with Sláinte! Margarita,Pot O'Gold, & Irish Old Fashioned <h4>3.Russian Samovar</h4> Alpensahne,Amarula Cream Liqueur, Caribou,Feni </Typography> </> ) }, { title: "Winner Winner Pizza Dinner", subheader: "May 14, 2021", imageUrl: "https://cdn.shopify.com/s/files/1/0624/9853/articles/20220211142645-margherita-9920.jpg?crop=center&height=800&v=1660843558&width=800", previewText: "Beat The Heat With Chilled Cocktails At The Best Bars In New York.", detailContent: ( <> <Typography paragraph>To a foodie, summer means one thing and one thing only – DRINKS!</Typography> <Typography paragraph> Yes people, we know it’s hot out there and the only way to quench your thirst is by guzzling down a bunch of ice-cold cocktails </Typography> <Typography paragraph> <h4>1.The Bar Room at The Beekman</h4> Visit the beautiful Bar Room in the historic Beekman Hotel for high-key romance that really wows. Do try the whiskey sour.One of the best drinks available. <h4>2.Dublin House</h4> You can never go wrong with Sláinte! Margarita,Pot O'Gold, & Irish Old Fashioned <h4>3.Russian Samovar</h4> Alpensahne,Amarula Cream Liqueur, Caribou,Feni </Typography> </> ) } ]; return ( <> <Grid container direction="row" justifyContent="center"> {blogData.map((blog, index) => ( <Grid item xs={4} key={index}> <BlogCard title={blog.title} subheader={blog.subheader} imageUrl={blog.imageUrl} previewText={blog.previewText} detailContent={blog.detailContent} /> </Grid> ))} </Grid> </> ); }
方案说明
- 每个
BlogCard组件内部维护独立的expanded状态,点击展开按钮仅影响当前卡片 - 卡片数据统一存入数组,通过
map循环渲染,减少重复代码,后续新增卡片只需添加数据项 - 保留原有UI样式和交互逻辑,同时提升代码的可维护性和扩展性
内容的提问来源于stack exchange,提问作者codertraveller
相关产品推荐
相关产品推荐

