关于 API 使用 .then 和 useEffect 获取订单的问题

Question about API fetch order using .then and useEffect

我有一个关于 API 使用 .then 和 useEffect 获取订单的问题。我会尽力向大家解释,以获得一些有用的建议。

代码的预期结果:

  1. 第一次加载时(第一次访问该站点时),我希望索引为 1 的神奇宝贝显示为默认值(因此,我设置默认值的原因状态变量“pokemonIndex”为 1。但没有任何显示..

  2. 每次单击将切换 toggle() 函数的按钮时,它都会呈现 getRandomIndex() 函数,更新状态变量“pokemonIndex”,这将 运行 useEffect .

  3. useEffect 将使用更新后的 ${pokemonIndex}

    获取新的 API
  4. 代码的其余部分似乎不言自明。

这些是我的问题:

  1. 在上面的第 1 步中,为什么即使我将默认状态变量设置为 1 也没有任何显示?

  2. 每次我点击 运行 的 toggle() 按钮时,它都会获取一个新的 API。但是,网络选项卡如下所示: chrome network tab image

-> 这是正常行为吗?或者我的代码有什么问题。感谢您阅读这么长的解释。提前致谢!

export default function Testing() {
    
    const [randomPokemon, setRandomPokemon] = React.useState({})
    const [specifics, setSpecifics] = React.useState({})
    const [pokemonIndex, setPokemonIndex] = React.useState(1)
    const [prevPokemonIndex, setPrevPokemonIndex] = React.useState(0)
    const [disableButton, setDisableButton] = React.useState(false)
    
    function disableBtn() {
        setDisableButton(prevState => !prevState);
        setTimeout((setDisableButton), 1500)
    }
    
    function getRandomIndex(lowerBound, upperBound) {
        return Math.floor(Math.random() * (upperBound - lowerBound + 1)) + lowerBound;
    };
    
        
    function toggle(e) {
        e.preventDefault()
        const randomIndex = getRandomIndex(1, 898);
        setPokemonIndex(randomIndex);
        disableBtn()
    }
    
    React.useEffect(() => {
        fetch(`https://pokeapi.co/api/v2/pokemon/${pokemonIndex}/`)
        .then(res => res.json())
        .then(data => {
            setRandomPokemon(data);
            setSpecifics({
                pokemonName: randomPokemon.name,
                pokemonId: randomPokemon.id,
                pokemonType: randomPokemon.types ? randomPokemon.types.map(type => type.type.name) : [],
                pokemonAbility: randomPokemon.abilities ? randomPokemon.abilities.map(ability => ability.ability.name) : [],
                pokemonImage: randomPokemon.sprites ? randomPokemon.sprites.other[`official-artwork`].front_default : []
            });
        })
    }, [pokemonIndex])
return (
        <div>
            <form>
                <button onClick={toggle} disabled={disableButton} type="button">Click me</button>
                <img src={specifics.pokemonImage}></img>
                <span>{specifics.pokemonName}</span>
                <span>{specifics.pokemonId}</span>
                <span>{specifics.pokemonType}</span>
                <span>{specifics.pokemonAbility}</span>
            </form>
        </div>
    )

这里的问题是您依赖 randomPokemon,状态原子,在 then 处理程序中。

但是React中的setState是异步的,所以randomPokemon不会在setRandomPokemon之后执行setSpecifics的时候发生变化,所以具体是始终根据效果调用时的数据计算(实际上 (heh) 是 just-loaded 数据后的一个“步骤”)。

您应该在该函数中使用 data(这是您刚刚加载的新数据)——或者,因为 specifics 是根据 randomPokemon 严格计算的,它根本不需要是状态原子,但可以使用 useMemo 导出,如下所示:

function getRandomIndex(lowerBound, upperBound) {
  return Math.floor(Math.random() * (upperBound - lowerBound + 1)) + lowerBound;
}

export default function Testing() {
  const [pokemon, setPokemon] = React.useState(null); // Nothing loaded yet
  const [pokemonIndex, setPokemonIndex] = React.useState(1); // Which pokemon to load
  const [disableButton, setDisableButton] = React.useState(false);

  function disableBtn() {
    setDisableButton(true);
    setTimeout(() => setDisableButton(false), 1500);
  }

  function loadRandomPokemon(e) {
    e.preventDefault();
    const randomIndex = getRandomIndex(1, 898);
    setPokemonIndex(randomIndex);
    disableBtn();
  }

  React.useEffect(() => {
    fetch(`https://pokeapi.co/api/v2/pokemon/${pokemonIndex}/`)
      .then((res) => res.json())
      .then(setPokemon);
  });

  const specifics = React.useMemo(() => {
    if (!pokemon) {
      return null; // Not loaded, no specifics yet
    }
    return {
      pokemonName: pokemon.name,
      pokemonId: pokemon.id,
      pokemonType: pokemon.types ? pokemon.types.map((type) => type.type.name) : [],
      pokemonAbility: pokemon.abilities ? pokemon.abilities.map((ability) => ability.ability.name) : [],
      pokemonImage: pokemon.sprites ? pokemon.sprites.other[`official-artwork`].front_default : [],
    };
  }, [pokemon]);

  // ...
}
React.useEffect(() => {
    fetch(`https://pokeapi.co/api/v2/pokemon/${pokemonIndex}/`)
    .then(res => res.json())
    .then(data => {
        setSpecifics({
            pokemonName: data.name,
            pokemonId: data.id,
            pokemonType: data.types ? data.types.map(type => type.type.name) : [],
            pokemonAbility: data.abilities ? data.abilities.map(ability => ability.ability.name) : [],
            pokemonImage: data.sprites ? data.sprites.other[`official-artwork`].front_default : []
        });

    })
}, [pokemonIndex])

试试这个。这对你有用。