React中按Item Key打开模态框及实现博客分页的问题求助
解决博客模态框显示重复数据及分页实现方案
我看到你现在遇到两个问题——打开模态框时总是显示相同的博客内容,以及需要实现每页10条的分页功能。咱们一步步来解决这些问题:
一、修复模态框显示重复数据的问题
问题根源
你现在的cardDialog方法会遍历所有博客,渲染每个博客对应的Dialog组件,而且所有Dialog的open状态都绑定到同一个this.state.openDialog。当你打开模态框时,所有Dialog都会被触发显示,但视觉上最后一个会覆盖前面的,所以看起来都是同一条数据。
解决方案
我们不需要渲染所有Dialog,只需要渲染当前选中的那一条博客的Dialog就可以了:
- 修改State结构:在state里添加
selectedBlog来存储当前选中的博客对象
constructor(props) { super(props); this.state = { modal: false, openDialog: false, selectedBlog: null // 新增:存储选中的博客 }; this.handleOpenDialog = this.handleOpenDialog.bind(this); this.handleCloseDialog = this.handleCloseDialog.bind(this); }
- 更新打开模态框的方法:点击"Read"按钮时,找到对应的博客对象并存入state
handleOpenDialog(id) { // 从博客数组中匹配选中的博客 const selectedBlog = this.props.resume.blogs.find(blog => blog._id === id); this.setState({ openDialog: true, selectedBlog: selectedBlog }); }
- 重构模态框渲染逻辑:只渲染当前选中的博客的Dialog
把原来的cardDialog方法替换成下面的逻辑:
renderDialog() { const { selectedBlog, openDialog } = this.state; if (!selectedBlog || !openDialog) return null; // 无选中博客或模态框关闭时不渲染 return ( <Dialog open={openDialog} className="open-dialog"> <FABButton onClick={this.handleCloseDialog} className="close-button" mini ripple> <Icon name="close" /> </FABButton> <DialogTitle>{selectedBlog.blog_short_desc}</DialogTitle> <img className="blogs-contents-image" src={selectedBlog.blog_image_link} alt="blogs"/> <h4>{selectedBlog.blog_name}</h4> <DialogContent className="dialog-contents">{selectedBlog.blog_desc}</DialogContent> </Dialog> ); }
- 在render中调用这个方法:替换原来的
{this.cardDialog(blogs, user)}
// ... 其他render内容 {this.renderDialog()} // ...
这样修改后,每次点击"Read"按钮只会渲染对应选中的博客的Dialog,不会再出现重复数据的问题。
二、实现每页10条的分页功能
步骤1:添加分页相关的State
constructor(props) { super(props); this.state = { modal: false, openDialog: false, selectedBlog: null, currentPage: 1, // 当前页码 postsPerPage: 10 // 每页显示条数 }; this.handleOpenDialog = this.handleOpenDialog.bind(this); this.handleCloseDialog = this.handleCloseDialog.bind(this); }
步骤2:计算当前页要显示的博客列表
在render方法里,根据当前页码和每页条数截取对应的数据:
render() { const { blogs, loading} = this.props.resume; const { currentPage, postsPerPage } = this.state; // 计算当前页的博客范围 const indexOfLastPost = currentPage * postsPerPage; const indexOfFirstPost = indexOfLastPost - postsPerPage; const currentBlogs = blogs.slice(indexOfFirstPost, indexOfLastPost); // 计算总页数 const totalPages = Math.ceil(blogs.length / postsPerPage); // ... 其他代码不变,把原来的blogs.map替换成currentBlogs.map <Grid id="todo"> {currentBlogs.map(({ _id, blog_name, blog_by, date }) => ( // ... 原来的Card内容保持不变 ))} </Grid> // ... }
步骤3:添加分页控制按钮
先定义一个分页组件,然后在render中调用:
// 分页控制组件(可以单独提取成文件) const Pagination = ({ currentPage, totalPages, paginate }) => { const pageNumbers = []; for (let i = 1; i <= totalPages; i++) { pageNumbers.push(i); } return ( <nav style={{ marginTop: '2rem', textAlign: 'center' }}> <ul style={{ listStyle: 'none', padding: 0, display: 'inline-flex', gap: '0.5rem' }}> {/* 上一页按钮 */} <li> <button onClick={() => paginate(currentPage - 1)} disabled={currentPage === 1} style={{ padding: '0.5rem 1rem', cursor: 'pointer', border: '1px solid #ddd', borderRadius: '4px' }} > 上一页 </button> </li> {/* 页码按钮 */} {pageNumbers.map(number => ( <li key={number}> <button onClick={() => paginate(number)} style={{ padding: '0.5rem 1rem', cursor: 'pointer', border: '1px solid #ddd', borderRadius: '4px', backgroundColor: currentPage === number ? '#007bff' : 'white', color: currentPage === number ? 'white' : 'black' }} > {number} </button> </li> ))} {/* 下一页按钮 */} <li> <button onClick={() => paginate(currentPage + 1)} disabled={currentPage === totalPages} style={{ padding: '0.5rem 1rem', cursor: 'pointer', border: '1px solid #ddd', borderRadius: '4px' }} > 下一页 </button> </li> </ul> </nav> ); };
然后在render里引入这个组件:
// ... 其他render内容 <Pagination currentPage={this.state.currentPage} totalPages={totalPages} paginate={(pageNumber) => this.setState({ currentPage: pageNumber })} /> // ...
这样就完成了分页功能,每页显示10条博客,点击页码可以切换页面。
小优化建议
- 当删除博客后,如果当前页数据为空,自动切换到上一页,避免出现空页面
- 可以把Pagination组件单独提取成独立文件,让代码结构更清晰
内容的提问来源于stack exchange,提问作者Tammy
相关产品推荐
相关产品推荐

