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

React Hooks实现搜索栏实时分页的问题

解决搜索结果实时分页的问题

看起来你遇到的核心问题是搜索结果的分页逻辑和页码计算没有基于过滤后的数据,而是一直沿用原始商品总数量,导致页码显示错误、切换页码时出现空数据的情况。我帮你梳理问题点并给出修复方案:

问题分析

  1. 分页数量计算错误:你的Pagination组件接收的totalData是原始data的长度,不是搜索过滤后的结果长度——哪怕搜索后只剩5条数据,页码还是会显示基于全部商品的数量。
  2. 搜索时未重置页码:如果当前在第3页,搜索后结果只有1页,此时分页的切片索引会超出过滤结果的长度,导致currentData为空。
  3. 过滤逻辑重复且未保存完整结果:每次调用filterData都重新过滤一次数据,既影响性能,也不利于分页时的索引计算。

修复步骤

1. 修正状态变量

先把filteredProduct的初始值改为数组,同时统一变量名(原代码里变量名和setter不一致,容易混淆):

const [filteredProducts, setFilteredProducts] = useState([]); // 替换原有的filteredProduct

2. 重构filterData函数

修改逻辑,先获取完整的过滤结果并保存,再基于过滤结果做分页切片:

function filterData() {
  let filteredResults;
  // 执行过滤逻辑
  if (searchTerm !== '') {
    filteredResults = data.filter(item => 
      item.articleName.toLowerCase().includes(searchTerm.toLowerCase())
    );
  } else {
    filteredResults = [...data];
  }
  // 保存完整的过滤结果,用于分页计算总数量
  setFilteredProducts(filteredResults);
  // 基于过滤后的结果计算当前页数据
  const indexOfLastData = currentPage * dataPerPage;
  const indexOfFirstData = indexOfLastData - dataPerPage;
  setCurrentData(filteredResults.slice(indexOfFirstData, indexOfLastData));
}

3. 搜索时重置页码

当搜索关键词变化时,自动回到第一页,避免索引越界:

useEffect(() => {
  setCurrentPage(1); // 搜索后重置到第一页
  filterData();
}, [searchTerm])

4. 更新分页组件的props

把分页组件的totalData改为使用过滤后的结果长度:

<Pagination 
  dataPerPage={dataPerPage} 
  totalData={filteredProducts.length} 
  paginate={paginate} 
/>

5. 优化依赖项(可选)

调整监听data、currentPage、dataPerPage的useEffect,处理空数据的情况:

useEffect(() => {
  // 动态生成列(避免data为空时报错)
  if (data.length > 0) {
    setColumns(
      Object.keys(data[0]).map(key => ({
        Header: key,
        accessor: key,
      }))
    );
  }
  filterData();
}, [data, currentPage, dataPerPage]);

修改后的完整App.js代码

import React, { useState, useEffect } from 'react';
import Styles from './Styles';
import Table from './Table';
import Pagination from './Pagination';

const App = () => {
  const [loading, setLoading] = useState(true);
  const [data, setData] = useState([]);
  const [currentData, setCurrentData] = useState([]);
  const [columns, setColumns] = useState([]);
  const [currentPage, setCurrentPage] = useState(1);
  const [dataPerPage, setDataPerPage] = useState(10);
  const [searchTerm, setSearchTerm] = useState("");
  const [filteredProducts, setFilteredProducts] = useState([]);

  useEffect(() => {
    var myHeaders = new Headers();
    myHeaders.append("Accept", "text/plain");
    myHeaders.append(
      "Authorization", "Bearer xxx"
    );
    var requestOptions = {
      method: "GET",
      headers: myHeaders,
      redirect: "follow",
    };
    fetch("http://localhost:5000/api/WarehousStock", requestOptions)
      .then((response) => response.json())
      .then((result) => {
        setData(result);
      })
      .catch((error) => console.log("error", error))
      .finally(() => setLoading(false));
  }, []);

  useEffect(() => {
    if (data.length > 0) {
      setColumns(
        Object.keys(data[0]).map((key) => ({
          Header: key,
          accessor: key,
        }))
      );
    }
    filterData();
  }, [data, currentPage, dataPerPage]);

  const paginate = (pageNumber) => setCurrentPage(pageNumber);

  function filterData() {
    let filteredResults;
    if (searchTerm !== '') {
      filteredResults = data.filter(item => 
        item.articleName.toLowerCase().includes(searchTerm.toLowerCase())
      );
    } else {
      filteredResults = [...data];
    }
    setFilteredProducts(filteredResults);
    const indexOfLastData = currentPage * dataPerPage;
    const indexOfFirstData = indexOfLastData - dataPerPage;
    setCurrentData(filteredResults.slice(indexOfFirstData, indexOfLastData));
  }

  useEffect(() => {
    setCurrentPage(1);
    filterData();
  }, [searchTerm])

  if (loading) return <p>Loading...</p>;

  return (
    <Styles>
      <div className="SearchButton">
        <input 
          type="text" 
          placeholder="Search name of product" 
          onChange={event => setSearchTerm(event.target.value)} 
        />
      </div>
      <Table columns={columns} data={currentData} />
      <Pagination 
        dataPerPage={dataPerPage} 
        totalData={filteredProducts.length} 
        paginate={paginate} 
      />
    </Styles>
  );
};

export default App;

为什么这些修改有效?

  • 保存完整的过滤结果:避免每次分页都重复执行过滤逻辑,同时给分页组件提供了准确的总数据量,确保页码数量正确。
  • 搜索时重置页码:保证搜索后从第一页开始展示结果,不会出现索引越界导致的空数据问题。
  • 分页组件使用过滤后的数据长度:让页码数量完全匹配搜索结果的实际数量,用户体验更合理。

内容的提问来源于stack exchange,提问作者user9624111

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.28 20:49:06