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

如何在React Query无限滚动中集成搜索功能?

结合搜索功能与React Query无限滚动

要将搜索功能与现有无限滚动结合,核心是让React Query根据搜索关键词重新触发分页查询,并重置分页状态。以下是具体实现步骤和修改后的代码:

关键修改点

  • 将搜索关键词加入Query Key:React Query通过queryKey区分不同查询,把搜索关键词加入后,每次搜索词变化时会自动清空旧分页数据,重新从第一页获取过滤结果。
  • 在分页查询中传递搜索关键词:修改queryFn,将搜索关键词传递给API请求,确保每次分页获取的都是过滤后的数据。
  • 实现搜索输入的状态更新:让搜索栏回调函数更新搜索关键词状态,触发React Query重新查询。
  • 可选:添加输入防抖:避免用户每输入一个字符就发起API请求,提升性能和体验。

修改后的完整代码

import { useState, useEffect } from 'react';
import { useInfiniteQuery } from '@tanstack/react-query';
import { useInView } from 'react-intersection-observer';

const CouponList = () => {
  const [query, setQuery] = useState<string>("");
  const [debouncedQuery, setDebouncedQuery] = useState(query);
  const { ref, inView } = useInView();

  // 防抖处理:用户停止输入300ms后再更新查询关键词
  useEffect(() => {
    const timer = setTimeout(() => {
      setDebouncedQuery(query);
    }, 300);
    return () => clearTimeout(timer);
  }, [query]);

  const { data, status, error, hasNextPage, fetchNextPage } = useInfiniteQuery({
    // 将防抖后的关键词加入queryKey,确保搜索变化时重置分页
    queryKey: ["coupons", debouncedQuery],
    // 传递页码和搜索关键词给API请求函数
    queryFn: ({ pageParam, queryKey }) => {
      const [_, searchQuery] = queryKey;
      return getPaginatedCoupons(pageParam, searchQuery);
    },
    initialPageParam: 1,
    staleTime: Infinity,
    refetchOnWindowFocus: false,
    getNextPageParam: (lastPage, allPages) => {
      // 只有当前页有数据时才返回下一页页码
      return lastPage?.length ? allPages.length + 1 : undefined;
    },
  });

  // 滚动到底部时加载下一页
  useEffect(() => {
    if (inView && hasNextPage) {
      fetchNextPage();
    }
  }, [inView, hasNextPage, fetchNextPage]);

  // 处理搜索栏的输入回调
  const handleCouponsFilter = (searchTerm: string) => {
    setQuery(searchTerm);
  };

  // 合并所有分页的数据
  const coupons = data?.pages.flat() || [];

  return (
    <div className="flex flex-col items-center justify-center mt-10">
      <div className="flex w-3/4 items-center justify-center">
        <SearchBar filter={handleCouponsFilter} />
      </div>
      <div className="grid grid-rows-1 md:grid-cols-12 lg:grid-cols-12 p-0 md:p-0 lg:p-0 justify-center items-center mt-11 justify-items-center">
        {coupons.map((coupon, index) => (
          <div
            key={coupon.id || index} // 优先使用coupon的唯一ID作为key,避免索引key的问题
            className="flex justify-center col-span-12 sm:col-span-12 md:col-span-4 w-full"
          >
            <CouponCard
              innerRef={index === coupons.length - 1 ? ref : undefined}
              card={coupon}
            />
          </div>
        ))}
      </div>
      {/* 可选:加载状态提示 */}
      {status === "loading" && <div>加载中...</div>}
      {error && <div>加载失败:{error.message}</div>}
    </div>
  );
};

// 示例API请求函数:接收页码和搜索关键词
const getPaginatedCoupons = async (page: number, searchQuery: string) => {
  const response = await fetch(`/api/coupons?page=${page}&query=${encodeURIComponent(searchQuery)}`);
  if (!response.ok) throw new Error("请求失败");
  return response.json();
};

export default CouponList;

额外说明

  • Query Key的作用:当debouncedQuery变化时,React Query会认为这是全新查询,自动清空之前的分页数据,重新从第一页获取结果。
  • 防抖的必要性:防抖可以减少不必要的API请求,避免给服务器造成压力,同时提升用户体验。
  • Key的优化:尽量使用数据的唯一ID作为key,而非数组索引,避免React渲染时出现不必要的重排。

内容的提问来源于stack exchange,提问作者E.Bolandian

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.02 14:58:11