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

React Query实现初始挂载与搜索切换数据的问题求助

解决React Query实现Pokédex搜索时数据自动回退的问题

问题原因分析

你的代码出现点击搜索后数据短暂变化又恢复的情况,核心原因有两个:

  1. Query Key缓存冲突:初始获取数据和搜索用了同一个queryKey: ["pokemons"],React Query会将这两个请求的缓存存储在同一key下。初始挂载的请求是自动启用的,当它完成后会覆盖搜索请求的缓存结果,导致数据回退。
  2. 过滤逻辑错误:你用map函数做过滤,会生成包含undefined的数组(不匹配的项会返回undefined),这不仅会导致渲染问题,也不符合过滤的预期。

另外,搜索功能没必要重新发起全量API请求,基于已获取的本地数据做过滤更高效,也能避免缓存冲突。

解决方案:本地过滤实现搜索

1. 重构全量数据获取Hook

确保使用唯一的Query Key,避免缓存冲突:

export const fetchPokemon = async (URL: string) => {
  const result = await axios.get<pokemon>(URL);
  return result;
};

export const fetchAllPokemons = async () => {
  const URL = "https://pokeapi.co/api/v2/pokemon?limit=100";
  const { data } = await axios.get<pokemons>(URL);
  const result = await Promise.all(
    data.results.map(async (pokemon) => fetchPokemon(pokemon.url))
  );
  return result;
};

export const useAllPokemons = () => {
  return useQuery({
    queryKey: ["all-pokemons"], // 唯一标识全量数据的Query Key
    queryFn: fetchAllPokemons,
  });
};

2. 组件内实现搜索逻辑

用useState存储搜索关键词,结合useMemo自动过滤数据(或点击按钮触发过滤):

import { useState, useMemo } from 'react';
import { useAllPokemons } from './your-hook-path';

const Pokedex = () => {
  const { data: allPokemons, isLoading } = useAllPokemons();
  const [searchTerm, setSearchTerm] = useState("");

  // 自动根据搜索关键词过滤数据,无需点击按钮
  const filteredPokemons = useMemo(() => {
    if (!allPokemons || !searchTerm.trim()) return allPokemons;
    
    // 过滤出名称以搜索词开头的宝可梦,忽略大小写
    return allPokemons.filter(pokemon => 
      pokemon.data.name.toLowerCase().startsWith(searchTerm.toLowerCase().trim())
    );
  }, [allPokemons, searchTerm]);

  // 如果需要点击按钮触发过滤,替换上面的useMemo为以下代码:
  // const [filteredPokemons, setFilteredPokemons] = useState(allPokemons);
  // const handleSearch = () => {
  //   if (!allPokemons) return;
  //   const filtered = allPokemons.filter(pokemon => 
  //     pokemon.data.name.toLowerCase().startsWith(searchTerm.toLowerCase().trim())
  //   );
  //   setFilteredPokemons(filtered);
  // };

  return (
    <div className="pokedex-container">
      <input
        type="text"
        value={searchTerm}
        onChange={(e) => setSearchTerm(e.target.value)}
        placeholder="输入宝可梦名称搜索..."
      />
      {/* 点击触发搜索的按钮(如果不用自动过滤则保留) */}
      {/* <button onClick={handleSearch}>搜索</button> */}

      {isLoading ? (
        <div>加载宝可梦数据中...</div>
      ) : (
        <div className="pokemon-list">
          {filteredPokemons?.map(pokemon => (
            <div key={pokemon.data.id} className="pokemon-card">
              <h3>{pokemon.data.name}</h3>
              <img src={pokemon.data.sprites.front_default} alt={pokemon.data.name} />
            </div>
          ))}
        </div>
      )}
    </div>
  );
};

export default Pokedex;

若需后端搜索的处理方式

如果你的搜索需求需要从API端获取结果(比如搜索海量数据),需为搜索请求设置包含关键词的唯一Query Key,避免和全量数据缓存冲突:

const [searchTerm, setSearchTerm] = useState("");

const { data: searchResults, isFetching: isSearching } = useQuery({
  queryKey: ["pokemon-search", searchTerm], // 将搜索关键词加入Query Key
  queryFn: async () => {
    // 示例:调用后端搜索接口(PokeAPI支持单个宝可梦名称查询,批量需自行处理)
    const response = await axios.get(`https://pokeapi.co/api/v2/pokemon/${searchTerm.toLowerCase()}`);
    return [response.data]; // 统一格式为数组,方便渲染
  },
  enabled: !!searchTerm.trim(), // 仅当有搜索词时发起请求
  onError: () => {
    // 处理搜索不到的情况
    console.log("未找到匹配的宝可梦");
  },
});

关键注意点

  • Query Key唯一性:不同的请求逻辑必须用不同的Query Key,避免React Query缓存互相覆盖。
  • 过滤逻辑正确性:用filter替代map做数据筛选,避免生成包含undefined的无效数组。
  • 优先本地过滤:已获取的数据直接在前端过滤,减少API请求次数和延迟。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.08 19:55:22