ReactJS分页组件异常:数据库4条数据仅显示页码1
分页组件仅显示单页码的问题排查与修复
问题描述
我开发了一个Pagination组件用于产品列表分页展示,数据库里有4条产品数据,但分页栏只显示页码1,完全无法正常分页,找不到问题出在哪。
相关代码
components/Pagination.jsx
import React from 'react' const Pagination = ({postsPerPage, totalPosts, paginate}) => { const pageNumbers = [] for(let i = 1; i <= Math.ceil(totalPosts / postsPerPage); i++){ pageNumbers.push(i) } return ( <div> <div className="pagination page navigation"> <ul className="Pagination-list"> {pageNumbers.map(number => <li key={number} className="page-item"> <a onClick={() => paginate(number)} href="#" className="page-link"> {number} </a> </li> )} </ul> </div> </div> ) } export default Pagination
Home页面代码
import React, { useEffect, useState } from 'react' import {client} from '../lib/client' import { motion } from 'framer-motion'; import { Product, FooterBanner, HeroBanner, Pagination } from '../components' const Home = ({products, bannerData}) => { // pagination const [currentPage, setCurrentPage] = useState(1) const [postsPerPage] = useState(1) const indexOfLastPost = currentPage * postsPerPage const indexofFirstPost = indexOfLastPost - postsPerPage const currentPosts = products.slice(indexofFirstPost, indexOfLastPost) const paginate = (pageNumber) => setCurrentPage(pageNumber) // search filter const [productItems, setProductItems] = useState([]) const [filterWork, setFilterWork] = useState([]); const [activeFilter, setActiveFilter] = useState('All'); const [animateCard, setAnimateCard] = useState({ y: 0, opacity: 1 }); useEffect(() => { setProductItems(currentPosts) setFilterWork(currentPosts) }, []) const handleProductFilter = (item) => { setActiveFilter(item) setAnimateCard([{ y: 100, opacity: 0 }]); setTimeout(() => { setAnimateCard([{ y: 0, opacity: 1 }]); if (item == 'All'){ setFilterWork(productItems) }else{ setFilterWork(productItems.slice(0).reverse().map((productItem)=> productItem.tags.includes(item))) } }, 500) } return ( <> <HeroBanner heroBanner={bannerData.length && bannerData[0]} /> <div className='products-heading'> <h2>Best Selling Products</h2> <p>Smoke accessories of many variations</p> </div> <div className='product_filter'> {['Lighter', 'Pipe', 'Roller', 'Hookah', 'All'].map((item, index) => ( <div key={index} className={`product_filter-item app__flex p-text ${activeFilter === item ? 'item-active' : ''}`} onClick={() => handleProductFilter(item)} > {item} </div> ))} </div> <motion.div animate={animateCard} transition={{ duration: 0.5, delayChildren: 0.5 }} > <div className='products-container'> { filterWork.map((product) => <Product key={product._id} product={product} /> )} </div> <Pagination postsPerPage={postsPerPage} totalPosts={filterWork.length} paginate={paginate} /> </motion.div> <FooterBanner footerBanner={bannerData && bannerData[0]} /> </> ) }; export const getServerSideProps = async () => { const query = '*[_type == "product"]' const products = await client.fetch(query) const bannerQuery = '*[_type == "banner"]' const bannerData = await client.fetch(bannerQuery) return { props: {products, bannerData} } } export default Home
问题根源
- 初始化逻辑未同步页码变化:
useEffect只在组件挂载时执行一次,没有监听currentPage和products的变化。切换页码时currentPosts会更新,但productItems和filterWork不会跟着变,导致filterWork.length始终是初始的1(因为postsPerPage设为1,第一页只有1条数据),分页组件自然只会算出1个页码。 - 过滤逻辑错误:
handleProductFilter的else分支用map返回布尔值数组,而不是过滤后的产品数组,不仅导致产品列表渲染异常,还会干扰分页总条数的计算。 - 分页总条数数据源错误:分页组件的
totalPosts用了filterWork.length,但filterWork只是当前页的数据,不是全量产品或过滤后的全量数据,无法正确计算总页码数。
修复方案
1. 修正useEffect的依赖与逻辑
让useEffect监听页码和全量产品的变化,每次切换页码或产品数据更新时,重新计算当前页数据并同步状态:
useEffect(() => { const indexOfLastPost = currentPage * postsPerPage; const indexofFirstPost = indexOfLastPost - postsPerPage; const currentPosts = products.slice(indexofFirstPost, indexOfLastPost); setProductItems(currentPosts); setFilterWork(currentPosts); }, [currentPage, products]);
2. 修复过滤逻辑
把map换成filter,正确返回符合标签条件的产品数组:
if (item == 'All'){ setFilterWork(productItems) }else{ // 用filter替代map,返回包含指定标签的产品 setFilterWork(productItems.filter(productItem => productItem.tags.includes(item))) }
3. 调整分页总条数的计算逻辑
如果需要基于全量数据过滤后再分页,建议重构状态逻辑,保存全量产品数据,过滤时基于全量数据计算,再切分当前页内容:
const Home = ({products, bannerData}) => { // 保存全量产品数据 const [allProducts, setAllProducts] = useState(products); // pagination const [currentPage, setCurrentPage] = useState(1) const [postsPerPage] = useState(1) // 搜索过滤 const [filterWork, setFilterWork] = useState([]); const [activeFilter, setActiveFilter] = useState('All'); const [animateCard, setAnimateCard] = useState({ y: 0, opacity: 1 }); // 基于全量数据、过滤条件和当前页码计算当前页数据 useEffect(() => { let filteredProducts = allProducts; if (activeFilter !== 'All') { filteredProducts = allProducts.filter(product => product.tags.includes(activeFilter)); } const indexOfLastPost = currentPage * postsPerPage; const indexofFirstPost = indexOfLastPost - postsPerPage; const currentPosts = filteredProducts.slice(indexofFirstPost, indexOfLastPost); setFilterWork(currentPosts); }, [currentPage, activeFilter, allProducts]); const handleProductFilter = (item) => { setActiveFilter(item) setAnimateCard({ y: 100, opacity: 0 }); setTimeout(() => { setAnimateCard({ y: 0, opacity: 1 }); // 切换过滤条件时重置页码到第一页 setCurrentPage(1); }, 500) } // 计算过滤后的总产品数,用于分页组件 const totalFilteredPosts = activeFilter === 'All' ? allProducts.length : allProducts.filter(product => product.tags.includes(activeFilter)).length; return ( <> {/* 其他代码不变 */} <motion.div animate={animateCard} transition={{ duration: 0.5, delayChildren: 0.5 }} > <div className='products-container'> {filterWork.map((product) => <Product key={product._id} product={product} />)} </div> <Pagination postsPerPage={postsPerPage} totalPosts={totalFilteredPosts} paginate={paginate} /> </motion.div> {/* 其他代码不变 */} </> ) };
4. 修复Pagination组件的跳转行为
给a标签添加阻止默认跳转的逻辑,避免页码切换时页面刷新:
<a onClick={(e) => { e.preventDefault(); paginate(number) }} href="#" className="page-link"> {number} </a>
内容的提问来源于stack exchange,提问作者Chidimma Nworah
相关产品推荐
相关产品推荐

