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

React Query客户端数据处理:搜索筛选UI不更新问题求助

问题分析

你的核心问题在于误用了React Query的select选项和queryKey实现前端筛选:

  1. 把searchQuery加入queryKey后,每次输入关键词都会触发React Query重新调用fetchWords请求后端,但如果fetchWords没有传递搜索参数给后端,返回的始终是全量词汇数据,属于不必要的重复请求。
  2. React Query的select函数仅在新数据被获取时重新执行:如果queryKey不变,缓存数据不会更新,即使searchQuery变化,select也不会重新运行筛选;而如果queryKey包含searchQuery,虽会重新请求,但前端筛选完全不需要依赖后端。
  3. 若展示卡片的组件也单独调用useGetWords,会导致Sidebar和展示组件的searchQuery状态隔离,输入的关键词无法同步到卡片筛选逻辑中。
解决方案

调整自定义Hook逻辑,基于全量缓存数据做前端实时筛选,而非依赖select或重复请求后端:

1. 修改useGetWords Hook

import { useState, useMemo } from 'react';
import { useQuery } from '@tanstack/react-query';

function useGetWords() {
  const [searchQuery, setSearchQuery] = useState('');

  const handleSearchQuery = (e: ChangeEvent<HTMLInputElement>) => {
    setSearchQuery(e.target.value);
  };

  // 仅请求一次全量词汇数据并缓存
  const {
    isLoading,
    data: allWords = [],
    error,
  } = useQuery({
    queryKey: ['words'], // 移除searchQuery,确保缓存全量数据
    queryFn: fetchWords,
    refetchOnWindowFocus: false,
    retry: 2,
  });

  // 基于全量数据和搜索关键词实时计算筛选结果,用useMemo优化性能
  const filteredWords = useMemo(() => {
    if (!searchQuery) return allWords;
    const lowerQuery = searchQuery.toLowerCase();
    return allWords.filter(word => 
      word.word.toLowerCase().includes(lowerQuery)
    );
  }, [allWords, searchQuery]);

  return { isLoading, words: filteredWords, error, searchQuery, handleSearchQuery };
}

export default useGetWords;

2. 确保状态共享(关键)

让所有需要使用筛选后词汇的组件(Sidebar和卡片展示组件)共享同一状态,避免各自维护独立的searchQuery:

// 父组件统一调用Hook,传递状态给子组件
function VocabApp() {
  const { isLoading, words, error, searchQuery, handleSearchQuery } = useGetWords();

  return (
    <div>
      <Sidebar 
        onDismiss={() => {/* 你的关闭逻辑 */}} 
        searchQuery={searchQuery} 
        handleSearchQuery={handleSearchQuery}
      />
      <VocabCards isLoading={isLoading} words={words} error={error} />
    </div>
  );
}

// 修改Sidebar组件,接收props而非单独调用Hook
function Sidebar({ onDismiss, searchQuery, handleSearchQuery }: SidebarProps) {
  return (
    <>
      <aside className="px-2 py-4 sm:w-[250px] sm:border-r">
        <div className="flex flex-col gap-3 mx-3">
          <Button
            onPress={onDismiss}
            className="w-full hover:bg-primary/60 text-stone-100"
            color="primary"
            startContent={<PlusCircleIcon className="w-5 h-5" />}
          >
            <span className="font-semibold capitalize">Add word</span>
          </Button>
          <Input
            placeholder="Search..."
            type="text"
            radius="md"
            startContent={<MagnifyingGlassIcon className="w-5 h-5" />}
            onChange={handleSearchQuery}
            value={searchQuery} {/* 绑定value,确保输入框受控 */}
          />
        </div>
        <p>{searchQuery}</p>
      </aside>
    </>
  );
}
关键优化点说明
  • 移除queryKey中的searchQuery:避免不必要的后端重复请求,全量数据仅请求一次并缓存。
  • 用useMemo替代select做筛选:useMemo会在allWords或searchQuery变化时重新计算结果,确保UI实时更新。
  • 统一状态管理:让相关组件共享同一searchQuery状态,避免状态隔离导致的UI不同步。
  • 受控输入框:给Input组件绑定value={searchQuery},确保输入框状态与Hook中的searchQuery完全同步。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.07 01:42:46