点击按钮打开Modal始终显示数组最后一个对象值的技术问题
解决点击卡片按钮打开Modal始终显示最后一个对象值的问题
问题根源
你遇到的问题是因为所有卡片中的Modal共享同一个open状态变量,而且循环结束后book变量会指向数组的最后一个元素。当你点击任意按钮打开Modal时,虽然视觉上只显示一个Modal,但实际上所有Modal都会尝试渲染,而此时book已经是最后一个对象,所以无论点哪个卡片,显示的都是最后一本书的数据。
解决方案一:用状态存储当前选中的书籍
这个方案不需要拆分组件,只需在父组件中维护当前选中的书籍对象:
- 修改状态定义,将原来的
open替换为selectedBook:
const [selectedBook, setSelectedBook] = useState(null);
- 更新打开和关闭Modal的函数:
const handleOpen = (book) => { setSelectedBook(book); }; const handleClose = () => { setSelectedBook(null); };
- 修改循环中的按钮点击事件和Modal属性:
<Grid container spacing={{ xs: 2, md: 3 }} > {postData.posts.map((book) => ( <Grid key={book.id}> {/* 记得加唯一key,避免React警告 */} <Card> {/* ... 卡片其他内容保持不变 ... */} <CardActions> <Button>Buy Now</Button> <div> <Button onClick={() => handleOpen(book)}>Open modal</Button> <Modal open={!!selectedBook} onClose={handleClose} aria-labelledby="modal-modal-title" aria-describedby="modal-modal-description" > {selectedBook && ( <Box sx={style}> <Typography id="modal-modal-title" variant="h6" component="h2"> {selectedBook.title} </Typography> <Typography>{selectedBook.author}</Typography> <Typography id="modal-modal-description" sx={{ mt: 2 }}> {selectedBook.detail} </Typography> <Rating name="size-medium" defaultValue={selectedBook.rating} /> </Box> )} </Modal> </div> </CardActions> </Card> </Grid> ))} </Grid>
解决方案二:将卡片封装为独立组件
把每个卡片和对应的Modal封装成单独的组件,每个组件维护自己的open状态,彻底避免状态共享问题:
- 创建
BookCard组件:
const BookCard = ({ book, style }) => { const [open, setOpen] = useState(false); const handleOpen = () => setOpen(true); const handleClose = () => setOpen(false); return ( <Card> <CardMedia component="img" height="240" image={`${book.url}`} alt="book cover" /> <CardContent> <Typography gutterBottom variant="h5" component="div"> {book.title} </Typography> <Typography>{book.author}</Typography> </CardContent> <CardActions> <Button>Buy Now</Button> <div> <Button onClick={handleOpen}>Open modal</Button> <Modal open={open} onClose={handleClose} aria-labelledby="modal-modal-title" aria-describedby="modal-modal-description" > <Box sx={style}> <Typography id="modal-modal-title" variant="h6" component="h2"> {book.title} </Typography> <Typography>{book.author}</Typography> <Typography id="modal-modal-description" sx={{ mt: 2 }}> {book.detail} </Typography> <Rating name="size-medium" defaultValue={book.rating} /> </Box> </Modal> </div> </CardActions> </Card> ); };
- 在父组件中循环渲染
BookCard:
<Grid container spacing={{ xs: 2, md: 3 }} > {postData.posts.map((book) => ( <Grid key={book.id}> <BookCard book={book} style={style} /> </Grid> ))} </Grid>
注意事项
- 记得给循环中的每个Grid元素添加唯一的
key属性(比如用book.id),这是React列表渲染的最佳实践,避免不必要的重渲染和警告。 - 如果使用方案一,Modal内部要加
selectedBook &&的判断,防止初始状态为null时出现渲染错误。
内容的提问来源于stack exchange,提问作者Mujakkir Ahmed
相关产品推荐
相关产品推荐

