You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

问题根源

  1. 初始化逻辑未同步页码变化:useEffect只在组件挂载时执行一次,没有监听currentPage和products的变化。切换页码时currentPosts会更新,但productItems和filterWork不会跟着变,导致filterWork.length始终是初始的1(因为postsPerPage设为1,第一页只有1条数据),分页组件自然只会算出1个页码。
  2. 过滤逻辑错误:handleProductFilter的else分支用map返回布尔值数组,而不是过滤后的产品数组,不仅导致产品列表渲染异常,还会干扰分页总条数的计算。
  3. 分页总条数数据源错误:分页组件的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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.13 07:50:26