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

React中多次API调用后如何等待UI更新?是否可使用useEffect实现?

问题解决方法

你当前需要点击三次才能加载完整数据的核心原因是React状态更新为异步操作:调用setPokemon后pokemon状态不会立刻更新,你在后续请求中直接读取pokemon.id、pokemonDescription.evolution拿到的都是上一次渲染的旧值,所以前两次点击参数都是空的,第三次才能拿到正确参数发起请求。
两种可行实现方案如下:

方案1:直接链式调用请求(更简洁,无需useEffect)

直接复用前一次请求返回的原始数据作为下一次请求的参数,不依赖state传参,一次点击即可跑完所有请求:

import Axios from "axios";
import React, { useState } from "react";

// axios实例移到组件外,避免每次渲染重复创建
const api = Axios.create({
  baseURL: "https://pokeapi.co/api/v2/",
});

function PK() {
  const [pokemon, setPokemon] = useState({});
  const [pokemonDescription, setPokemonDescription] = useState({});
  const [evolution, setEvolution] = useState({});
  const [loading, setLoading] = useState(false); // 加载状态提升用户体验

  const searchPokemon = async () => {
    try {
      setLoading(true);
      // 第一步:请求基础宝可梦数据
      const pokemonRes = await api.get(`pokemon/magnemite`);
      const basePokemon = {
        name: pokemonRes.data.name,
        height: pokemonRes.data.height,
        weight: pokemonRes.data.weight,
        img: pokemonRes.data.sprites.front_default,
        id: pokemonRes.data.id,
        type: pokemonRes.data.types[0].type.name,
        // 加可选链避免单属性宝可梦报错
        type2: pokemonRes.data.types[1]?.type.name || '',
      };

      // 第二步:直接用第一步返回的id发请求,不用等state更新
      const speciesRes = await api.get(`pokemon-species/${basePokemon.id}/`);
      const description = {
        entry: speciesRes.data.flavor_text_entries[0].flavor_text,
        evolutionUrl: speciesRes.data.evolution_chain.url,
      };

      // 第三步:直接用第二步返回的进化链url发请求
      const evolutionRes = await api.get(description.evolutionUrl);
      const evolutionData = {
        // 加可选链避免最终形态宝可梦无进化链报错
        evolution: evolutionRes.data.chain.evolves_to[0]?.species.name || '无进化',
      };

      // 统一更新所有状态,也可每步请求完成后单独更新
      setPokemon(basePokemon);
      setPokemonDescription(description);
      setEvolution(evolutionData);
    } catch (err) {
      console.error('请求失败:', err);
    } finally {
      setLoading(false);
    }
  };

  return (
    <div>
      <h1 style={{ textTransform: "capitalize" }}>{pokemon.name}</h1>
      <h1>#{pokemon.id}</h1>
      <h1>{pokemon.weight / 10} Kg</h1>
      <h1>{pokemon.height * 10} Cm</h1>
      <img src={pokemon.img} alt={pokemon.name} />
      <h1 style={{ textTransform: "capitalize" }}>
        Type: {pokemon.type} {pokemon.type2}
      </h1>
      <h2 style={{ textTransform: "capitalize" }}>
        {pokemonDescription.entry}
      </h2>
      <h1 style={{ textTransform: "capitalize" }}>
        Evolution: {evolution.evolution}
      </h1>
      <button onClick={searchPokemon} disabled={loading}>
        {loading ? '加载中...' : '点击加载宝可梦信息'}
      </button>
    </div>
  );
}
export default PK;

方案2:使用useEffect监听状态触发后续请求

如果你需要用useEffect实现,可以监听前一步返回的状态作为依赖,自动触发后续请求:

import Axios from "axios";
import React, { useState, useEffect } from "react";

const api = Axios.create({
  baseURL: "https://pokeapi.co/api/v2/",
});

function PK() {
  const [pokemon, setPokemon] = useState({});
  const [pokemonDescription, setPokemonDescription] = useState({});
  const [evolution, setEvolution] = useState({});
  const [triggerFetch, setTriggerFetch] = useState(false); // 点击按钮触发首次请求

  // 第一步:监听触发状态,请求基础宝可梦数据
  useEffect(() => {
    if (!triggerFetch) return;
    api.get(`pokemon/magnemite`).then((response) => {
      setPokemon({
        name: response.data.name,
        height: response.data.height,
        weight: response.data.weight,
        img: response.data.sprites.front_default,
        id: response.data.id,
        type: response.data.types[0].type.name,
        type2: response.data.types[1]?.type.name || '',
      });
    });
  }, [triggerFetch]);

  // 第二步:监听pokemon.id变化,请求物种描述数据
  useEffect(() => {
    if (!pokemon.id) return;
    api.get(`pokemon-species/${pokemon.id}/`).then((response) => {
      setPokemonDescription({
        entry: response.data.flavor_text_entries[0].flavor_text,
        evolutionUrl: response.data.evolution_chain.url,
      });
    });
  }, [pokemon.id]);

  // 第三步:监听evolutionUrl变化,请求进化链数据
  useEffect(() => {
    if (!pokemonDescription.evolutionUrl) return;
    api.get(pokemonDescription.evolutionUrl).then((response) => {
      setEvolution({
        evolution: response.data.chain.evolves_to[0]?.species.name || '无进化',
      });
    });
  }, [pokemonDescription.evolutionUrl]);

  return (
    <div>
      <h1 style={{ textTransform: "capitalize" }}>{pokemon.name}</h1>
      <h1>#{pokemon.id}</h1>
      <h1>{pokemon.weight / 10} Kg</h1>
      <h1>{pokemon.height * 10} Cm</h1>
      <img src={pokemon.img} alt={pokemon.name} />
      <h1 style={{ textTransform: "capitalize" }}>
        Type: {pokemon.type} {pokemon.type2}
      </h1>
      <h2 style={{ textTransform: "capitalize" }}>
        {pokemonDescription.entry}
      </h2>
      <h1 style={{ textTransform: "capitalize" }}>
        Evolution: {evolution.evolution}
      </h1>
      <button onClick={() => setTriggerFetch(true)}>点击加载宝可梦信息</button>
    </div>
  );
}
export default PK;

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.20 19:27:04