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

如何在依赖前置查询时正确使用useQueries获取Pokémon数据?

问题:React Query useQueries 正确使用方式

我正在开发一个Web应用,初始页面加载时通过PokéAPI展示宝可梦列表,列表用MUI Card显示每个宝可梦的图片和名称。

我用到两个API:

  • 获取宝可梦名称及基础列表的API
  • 获取特定宝可梦图片详情的API

实现逻辑是先调用第一个API拿到列表,再用列表里的每个宝可梦的URL调用第二个API获取图片。

下面是我写的两个组件:

PokemonList.jsx

import Box from "@mui/material/Box";
import { useQueries, useQuery } from "@tanstack/react-query";
import axios from "axios";

import PokemonCard from "./PokemonCard";

const getPokemonList = async () => {
  const { data } = await axios.get(
    "https://pokeapi.co/api/v2/pokemon?limit=12&offset=0"
  );
  console.log(data);
  return data;
};

const usePokemonList = () => {
  return useQuery(["pokemonList"], () => getPokemonList());
};

const getPokemons = async (url) => {
  const { data } = await axios.get(url);
  return data;
};

const usePokemons = (pokemons) => {
  return useQueries({
    queries: pokemons?.results.map((pokemon) => {
      return {
        queryKey: ["pokemons", pokemon.name],
        queryFn: getPokemons.bind(this, pokemon.url),
        staleTime: Infinity,
        enabled: !!pokemons.count,
      };
    }),
  });
};

function PokemonList() {
  const { data: pokemonList } = usePokemonList();
  const { data: pokemons, isLoading } = usePokemons(pokemonList);
  return (
    <Box
      sx={{
        display: "flex",
        justifyContent: "space-evenly",
        flexFlow: "row wrap",
        gap: "2em",
      }}
    >
      {!isLoading &&
        pokemons.map((pokemon) => (
          <PokemonCard
            key={pokemon.species.name}
            name={pokemon.species.name}
            image={pokemon.sprites.front_default}
          />
        ))}
    </Box>
  );
}

export default PokemonList;

PokemonCard.jsx

import { Card, CardContent, CardMedia, Typography } from "@mui/material";
import PropTypes from "prop-types";

PokemonCard.propTypes = {
  image: PropTypes.string,
  name: PropTypes.string,
};

function PokemonCard(props) {
  return (
    <Card sx={{ width: 225, flexWrap: "wrap" }}>
      <CardMedia component="img" height="140" image={props.image} />
      <CardContent>
        <Typography variant="caption">{props.name}</Typography>
      </CardContent>
    </Card>
  );
}

export default PokemonCard;

现在遇到的问题是第一个API还没返回结果时就触发了第二个API调用,导致报错。请问这里正确使用useQueries的方式是什么?


解决方案

问题核心在于对useQueries的返回值结构理解有误,且未正确处理查询的加载状态,修正后的代码如下:

修正后的 usePokemons 钩子

const usePokemons = (pokemonList) => {
  return useQueries({
    queries: pokemonList?.results?.map((pokemon) => ({
      queryKey: ["pokemons", pokemon.name],
      queryFn: () => getPokemons(pokemon.url),
      staleTime: Infinity,
      enabled: !!pokemonList,
    })) || [],
  });
};

修正后的 PokemonList 组件

function PokemonList() {
  const { data: pokemonList, isLoading: listLoading } = usePokemonList();
  const pokemonQueries = usePokemons(pokemonList);

  // 过滤已成功获取的宝可梦数据
  const pokemons = pokemonQueries
    .filter(query => query.data)
    .map(query => query.data);
  // 判断所有子查询是否加载完成
  const allLoaded = pokemonQueries.every(query => !query.isLoading);

  if (listLoading) {
    return <div>加载列表中...</div>;
  }

  return (
    <Box
      sx={{
        display: "flex",
        justifyContent: "space-evenly",
        flexFlow: "row wrap",
        gap: "2em",
      }}
    >
      {pokemons.map((pokemon) => (
        <PokemonCard
          key={pokemon.id}
          name={pokemon.name}
          image={pokemon.sprites.front_default}
        />
      ))}
      {!allLoaded && <div>加载宝可梦详情中...</div>}
    </Box>
  );
}

关键修改点说明

  1. useQueries返回值处理:它返回的是单个查询状态的数组,每个元素包含data、isLoading等属性,不能直接解构全局的data和isLoading。
  2. 子查询启用条件:将enabled设为!!pokemonList,确保只有第一个查询返回结果后才执行子查询。
  3. 加载状态分层处理:分别处理列表加载和子查询加载的状态,避免数据未就绪时执行map操作。
  4. 简化queryFn:用箭头函数直接传递URL,无需bind绑定上下文。
  5. 更可靠的key:改用宝可梦的id作为列表项key,比名称更唯一稳定。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.11 21:25:18