如何在React中为搜索结果整合分页功能(MERN栈)
解决方案:整合MERN栈的搜索与分页功能
明确结论:单个URL完全可以同时携带搜索与分页参数
比如/posts/?searches=react&page=2,这是HTTP GET请求的标准用法,后端可通过req.query同时获取两个参数。
1. 后端(Node.js)代码优化
你的现有代码已在搜索分支加入分页逻辑,但缺少返回总数据条数的逻辑(前端需此计算总页数),同时完善错误处理:
const DEFAULT_PAGE_NUMBER = 1; const DEFAULT_PAGE_LIMIT = 12; const getPagination = (query) => { const page = Math.abs(parseInt(query.page)) || DEFAULT_PAGE_NUMBER; const limit = Math.abs(parseInt(query.limit)) || DEFAULT_PAGE_LIMIT; const skip = (page - 1) * limit; return { skip, limit, page }; }; const getAllPosts = async (req, res) => { try { const { skip, limit, page } = getPagination(req.query); const { searches } = req.query; // 构建查询条件 let queryCondition = {}; if (searches) { queryCondition.title = { $regex: searches.toString(), $options: "i" // 不区分大小写 }; } // 并行查询数据列表和总条数,提升效率 const [posts, totalCount] = await Promise.all([ Post.find(queryCondition) .populate('username', 'username') .sort({ createdAt: -1 }) .skip(skip) .limit(limit), Post.countDocuments(queryCondition) ]); // 返回包含分页信息的响应 res.status(200).json({ posts, currentPage: page, totalPages: Math.ceil(totalCount / limit), totalCount }); } catch (err) { res.status(500).json({ error: '获取数据失败:' + err.message }); } };
2. 前端(React.js)整合搜索与分页逻辑
将搜索关键词、当前页码作为状态统一管理,合并数据请求逻辑:
import { useState, useEffect } from 'react'; import axios from 'axios'; const PostList = () => { const [posts, setPosts] = useState([]); const [searchTerm, setSearchTerm] = useState(''); const [currentPage, setCurrentPage] = useState(1); const [totalPages, setTotalPages] = useState(1); const [isLoading, setIsLoading] = useState(false); // 统一的请求函数,同时处理搜索和分页参数 const fetchPosts = async () => { setIsLoading(true); try { // 用URLSearchParams构建参数,自动处理特殊字符编码 const params = new URLSearchParams(); if (searchTerm.trim()) params.append('searches', searchTerm.trim()); params.append('page', currentPage); const response = await axios.get('/posts/', { params }); setPosts(response.data.posts); setTotalPages(response.data.totalPages); } catch (err) { console.error('请求失败:', err); } finally { setIsLoading(false); } }; // 搜索关键词变化时,重置页码为1(避免停留在无数据的页码) useEffect(() => { setCurrentPage(1); }, [searchTerm]); // 页码或搜索词变化时,重新拉取数据 useEffect(() => { fetchPosts(); }, [currentPage, searchTerm]); // 切换页码的处理函数 const handlePageChange = (newPage) => { if (newPage >= 1 && newPage <= totalPages) { setCurrentPage(newPage); } }; return ( <div className="post-container"> {/* 搜索输入框 */} <div className="search-bar"> <input type="text" value={searchTerm} onChange={(e) => setSearchTerm(e.target.value)} placeholder="搜索文章标题..." /> </div> {/* 文章列表区域 */} <div className="posts-list"> {isLoading ? ( <p>加载中...</p> ) : posts.length === 0 ? ( <p>暂无相关文章</p> ) : ( posts.map(post => ( <div key={post._id} className="post-card"> <h3>{post.title}</h3> <p className="author">作者:{post.username.username}</p> </div> )) )} </div> {/* 分页控件 */} {totalPages > 1 && ( <div className="pagination"> <button onClick={() => handlePageChange(currentPage - 1)} disabled={currentPage === 1 || isLoading} > 上一页 </button> {/* 生成页码按钮 */} {Array.from({ length: totalPages }, (_, idx) => idx + 1).map(page => ( <button key={page} onClick={() => handlePageChange(page)} className={currentPage === page ? 'active' : ''} disabled={isLoading} > {page} </button> ))} <button onClick={() => handlePageChange(currentPage + 1)} disabled={currentPage === totalPages || isLoading} > 下一页 </button> </div> )} </div> ); }; export default PostList;
关键说明
- 参数传递:用
URLSearchParams构建查询参数,比手动拼接字符串更安全,能自动处理空格、特殊字符的URL编码; - 状态联动:搜索关键词变化时重置页码为1,避免用户搜索后停留在无对应数据的页码;
- 性能优化:后端用
Promise.all并行查询数据列表和总条数,减少数据库查询时间; - 用户体验:加入加载状态、空数据提示,禁用无效分页按钮,提升交互体验。
内容的提问来源于stack exchange,提问作者user19263124
相关产品推荐
相关产品推荐

