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

React中循环调用PokeApi时数据异常的问题求助

问题:循环调用PokeApi获取宝可梦数据不完整、返回不稳定

拥有一份宝可梦名称列表,需要循环调用PokeApi获取数据并展示,但输出数据不完整,返回值不稳定,怀疑是异步同步或数据覆盖问题导致,尝试多种方法未解决。相关代码如下:

原代码

App.tsx

import { gen1List } from 'data';
import React from 'react';
import { Pokemon } from 'models';
import { useFetchPokemon } from 'api/useFetchPokemon';

export const App = () => {
    const gen1Pokemon = useFetchPokemon(gen1List);

    return (
        <>
            {gen1Pokemon.map((pokemon: Pokemon, index: number) => (
                <div key={index}>
                    {pokemon.name}
                    <img src={pokemon.sprite} alt={pokemon.name} />
                </div>
            ))}
        </>
    );
};

useFetchPokemon.ts

import { Pokemon, filterPokemonData } from "models";
import { useEffect, useState } from "react"
import { fetchPokemon } from "api/fetchPokemon";

export const useFetchPokemon = (pokemonList: string[]) => {
    const [pokemon, setPokemon] = useState<Pokemon[]>([]);
    const pokemonArray: Pokemon[] = [];

    useEffect(() => {
        for (const mon of pokemonList) {
            fetchPokemon(mon).then((result) => {
                pokemonArray.push(filterPokemonData(result));
            });
        };

        setPokemon(pokemonArray);
    }, []);

    return pokemon;
};

fetchPokemon.ts

export const fetchPokemon = async (name: string): Promise<any> => {
    const response = await fetch('https://pokeapi.co/api/v2/pokemon/' + name);
    return await response.json();
};

models.ts

export interface Pokemon {
    name: string;
    sprite: string;
};

export const filterPokemonData = (pokemon): Pokemon => {
    return {
        name: pokemon.name,
        sprite: pokemon.sprites.front_default,
    }
};

data.ts

export const gen1List: string[] = [
    'bulbasaur',
    'venusaur',
    'charizard',
];

问题根源

  1. 异步时序错误:useFetchPokemon中,循环发起异步请求后立刻调用setPokemon(pokemonArray),此时所有fetchPokemon请求都还未返回,pokemonArray是空数组;后续请求返回后修改数组也不会触发状态更新。
  2. 闭包与状态更新逻辑问题:pokemonArray是useEffect内部的局部变量,请求返回后向其中push数据不会触发组件重新渲染;且setPokemon仅执行一次,后续数组变化无法同步到状态中。
  3. 依赖项缺失:useEffect的依赖数组为空,当pokemonList变化时不会重新触发请求,同时违反React Hook的依赖规则。

解决方案

核心思路是用Promise.all等待所有异步请求完成后统一更新状态,同时修正依赖项,避免闭包问题:

修改后的useFetchPokemon.ts

import { Pokemon, filterPokemonData } from "models";
import { useEffect, useState } from "react"
import { fetchPokemon } from "api/fetchPokemon";

export const useFetchPokemon = (pokemonList: string[]) => {
    const [pokemon, setPokemon] = useState<Pokemon[]>([]);
    const [isLoading, setIsLoading] = useState(true);

    useEffect(() => {
        const fetchAllPokemon = async () => {
            setIsLoading(true);
            try {
                // 并行发起所有请求,统一处理结果
                const pokemonPromises = pokemonList.map(mon => 
                    fetchPokemon(mon).then(filterPokemonData)
                );
                const fetchedPokemon = await Promise.all(pokemonPromises);
                setPokemon(fetchedPokemon);
            } catch (error) {
                console.error("获取宝可梦数据失败:", error);
            } finally {
                setIsLoading(false);
            }
        };

        fetchAllPokemon();
    }, [pokemonList]); // 添加列表到依赖,确保列表变化时重新请求

    return { pokemon, isLoading };
};

对应优化后的App.tsx

import { gen1List } from 'data';
import React from 'react';
import { Pokemon } from 'models';
import { useFetchPokemon } from 'api/useFetchPokemon';

export const App = () => {
    const { pokemon: gen1Pokemon, isLoading } = useFetchPokemon(gen1List);

    if (isLoading) {
        return <div>加载中...</div>;
    }

    return (
        <>
            {gen1Pokemon.map((pokemon: Pokemon) => (
                <div key={pokemon.name}> {/* 用唯一name作为key,避免index带来的渲染问题 */}
                    {pokemon.name}
                    <img src={pokemon.sprite} alt={pokemon.name} />
                </div>
            ))}
        </>
    );
};

关键优化点

  • 使用Promise.all并行处理所有请求,确保所有数据返回后再更新状态,彻底解决数据不完整问题。
  • 将请求逻辑封装在异步函数内,用await保证时序正确,避免闭包导致的状态不同步。
  • 添加pokemonList到useEffect依赖数组,符合React Hook规则,同时支持列表动态变化时重新请求。
  • 新增加载状态与错误捕获,提升用户体验与代码健壮性。
  • 替换key为宝可梦名称,避免使用index带来的组件重渲染错误。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.10 20:05:34