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

如何在useInfiniteQuery回调中调用React自定义Hook useSearcher?

问题

我有一个用于拉取数据并处理其他逻辑的自定义Hook:

const search = useSearcher(...);

该Hook会发送API请求并按需更新搜索状态——当搜索查询为空字符串时清空搜索状态、取消请求、请求成功时设置搜索结果、设置加载状态、请求成功但无匹配结果的状态等。search对象包含完整的搜索状态:

{
  searchResults, //T[]
  isLoading, //boolean
  isNoMatchingResult, //boolean
  searchInputValue, //string
  resetSearchResults,// ()=>void
  ...
}

我尝试将其用于为useInfiniteQuery拉取数据,代码如下:

const { status, data, error, isFetching, isFetchingNextPage, fetchNextPage, hasNextPage } =
    useInfiniteQuery('projects', (ctx) => useSearcher(ctx.pageParam), {
      getNextPageParam: (_lastGroup, groups) => groups.length,
    });

此时出现报错:

React Hook "useSearcher" cannot be called inside a callback. React Hooks must be called in a React function component or a custom React Hook function

我清楚Hook不能在回调中调用,只能直接在组件或自定义Hook中使用,但该如何解决这个问题?除了将useSearcher中的数据请求代码提取为独立方法外,还有其他方案吗?

解决方案

除了提取独立请求方法外,还有以下两种可行方案:

1. 封装适配useInfiniteQuery的自定义Hook

把useSearcher和useInfiniteQuery的逻辑整合到一个新的自定义Hook中,在顶层调用useSearcher,再复用它的请求能力配合分页逻辑:

const useInfiniteSearcher = (initialPageParam) => {
  // 在Hook顶层调用useSearcher,遵守Hook规则
  const { searchResults, isLoading, resetSearchResults, ...restSearchState } = useSearcher(initialPageParam);
  
  // 用useInfiniteQuery处理分页逻辑
  const infiniteQueryResult = useInfiniteQuery(
    'infinite-search',
    async (ctx) => {
      // 假设useSearcher暴露了底层请求方法,传入分页参数拉取数据
      const nextPageResults = await restSearchState.fetchData(ctx.pageParam);
      return nextPageResults;
    },
    {
      getNextPageParam: (_lastGroup, groups) => groups.length,
      initialPageParam
    }
  );

  // 合并返回状态,整合两个Hook的能力
  return {
    ...infiniteQueryResult,
    ...restSearchState,
    isLoading: isLoading || infiniteQueryResult.isFetching,
    searchResults: infiniteQueryResult.data?.pages.flat() || searchResults
  };
};

使用时直接在组件顶层调用这个新Hook:

const { data, isFetchingNextPage, fetchNextPage, hasNextPage, isLoading } = useInfiniteSearcher(0);

2. 修改useSearcher支持动态分页参数

调整useSearcher的逻辑,允许外部传入分页参数并触发新请求,再配合useInfiniteQuery的分页状态驱动更新:

// 修改后的useSearcher,新增分页参数管理逻辑
const useSearcher = ({ initialQuery, initialPageParam }) => {
  const [pageParam, setPageParam] = useState(initialPageParam);
  const [searchResults, setSearchResults] = useState([]);
  const [isLoading, setIsLoading] = useState(false);
  const [hasNextPage, setHasNextPage] = useState(true);
  // ...原有其他状态

  // 分页参数变化时触发追加请求
  useEffect(() => {
    if (pageParam === initialPageParam) return;
    
    const fetchNextPage = async () => {
      setIsLoading(true);
      try {
        const nextResults = await api.fetchData({ query: searchInputValue, page: pageParam });
        setSearchResults(prev => [...prev, ...nextResults]);
        setHasNextPage(nextResults.length > 0);
      } catch (err) {
        // 错误处理逻辑
      } finally {
        setIsLoading(false);
      }
    };
    fetchNextPage();
  }, [pageParam, searchInputValue]);

  // 暴露更新分页参数的方法
  const updatePageParam = (newPage) => setPageParam(newPage);

  return {
    searchResults,
    isLoading,
    hasNextPage,
    updatePageParam,
    // ...原有其他返回状态
  };
};

然后在组件中配合使用两个Hook,用useInfiniteQuery管理分页流程:

const { searchResults, isLoading, updatePageParam, hasNextPage } = useSearcher({ initialQuery: '', initialPageParam: 0 });

// useInfiniteQuery仅负责分页状态管理,实际请求由useSearcher处理
const { fetchNextPage } = useInfiniteQuery(
  'projects',
  async (ctx) => {
    updatePageParam(ctx.pageParam);
    // 返回当前页切片结果,供useInfiniteQuery维护分页结构
    return searchResults.slice(ctx.pageParam * 10, (ctx.pageParam + 1) * 10);
  },
  {
    getNextPageParam: (_lastGroup, groups) => groups.length,
    initialPageParam: 0,
    // 禁用useInfiniteQuery自带的自动请求,由useSearcher控制
    enabled: false
  }
);

注意事项

  • 第一种方案适合useSearcher已暴露底层请求方法的场景,职责分离清晰,不需要大幅修改原有代码。
  • 第二种方案需要调整原useSearcher的逻辑,适合希望保留原有状态管理、不想拆分请求代码的场景。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.11 19:20:21