【问题标题】:React: adding objects to state arrayReact:将对象添加到状态数组
【发布时间】:2021-05-02 11:26:32
【问题描述】:

我正在尝试使用 forEach 循环将多个对象添加到状态数组:

const App = () => {
const [pokemon, setPokemon] = useState([]);
axios.get('https://pokeapi.co/api/v2/pokemon') // Gives me 20 pokemon
.then(res => {
  res.data.results.map(p => p.url).forEach(url => { // Loops over endpoints of pokemon
    axios.get(url)
    .then(response => {
      setPokemon(response.data)
    })
  });
})
}

您可能已经猜到了,当我 console.log pokemon 时,只显示 forEach 循环中的最后一项,因为它是最后一个要设置为状态的项。

我正在使用的 api:https://pokeapi.co/

我首先调用https://pokeapi.co/api/v2/pokemon,它给了我 20 个 pokemon,每个 pokemon 对象都带有一个端点以获取有关它的更多信息,我想将这些端点的信息存储为状态中的对象。

感谢您的帮助,如果您知道更好的方法,请随时告诉我。

【问题讨论】:

    标签: javascript reactjs api axios


    【解决方案1】:

    我建议你在 useEffect() 钩子中使用 Promise.all()。通过使用 useEffect,您可以在组件挂载时运行一次获取代码(而不是每次呈现时)。使用Promise.all() 允许您传递promises 的数组(这是axios.get() 返回的内容),并将每个Promise 解析为一个。这个 Promise 可以解析为每个 axios.get() 调用导致的一系列响应。这允许您只设置一次状态。

    查看工作示例:

    const {useState, useEffect} = React;
    const App = () => {
      const [pokemon, setPokemon] = useState([]);
      
      useEffect(() => {
        axios.get('https://pokeapi.co/api/v2/pokemon')
          .then(res => Promise.all(res.data.results.map(pokemon => axios.get(pokemon.url))))
          .then(arr => setPokemon(arr.map(response => response.data)));
      }, []);
      
      return (<div>
        {pokemon.map(({id, species}) => <p key={id}>{species.name}</p>)}
      </div>);
    }
    
    ReactDOM.render(<App />, document.body);
    <script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.0/umd/react.production.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.0/umd/react-dom.production.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/axios/0.21.1/axios.min.js" integrity="sha512-bZS47S7sPOxkjU/4Bt0zrhEtWx0y0CRkhEp8IckzK+ltifIIE9EMIMTuT/mEzoIMewUINruDBIR/jJnbguonqQ==" crossorigin="anonymous"></script>

    【讨论】:

    • 我有这些功能,我可能需要在第一个 .then 内触发,但我似乎无法在不破坏的情况下做到这一点:setLoading(false) setNextPageUrl(res.data.next) setPrevPageUrl(res.data.previous)
    • @A.Copland 你可以创建一个body {},然后返回Promise.all的结果,你可以把你的函数调用放在return之前:.then(res =&gt; {setLoading(false); ... return Promise.all(res.data.results.map(pokemon =&gt; axios.get(pokemon.url)))})
    • 感谢@Nick Parsons 的款待。
    【解决方案2】:

    只需创建一个临时数组,将您从 API 获得的所有数据添加到该数组中,然后使用该变量设置状态。您不必每次都设置状态 - 您可以一次设置。

    const App = () => {
      const [pokemon, setPokemon] = useState([]);
      const tempArr = [];
      axios.get('https://pokeapi.co/api/v2/pokemon') // Gives me 20 pokemon
      .then(res => {
        res.data.results.map(p => p.url).forEach(url => { // Loops over endpoints of pokemon
          axios.get(url).then(response => {
            tempArr.push(response.data);
          })
        });
      });
      setPokemon(tempArr);
    }
    

    【讨论】:

    • 这将在任何东西被推送到tempArr之前调用setPokemon(tempArr);
    • 所以这看起来很有效,我将它记录到控制台,我可以看到 4 个不同的数组,3 个什么都没有,但最后一个全部都在。感谢您的帮助
    • @NickParsons 是的,你是对的。我对 JS 相当陌生,我倾向于以同步格式考虑代码执行。使用 axios.all() 代替内部 axios 并在 axios.all 调用的 .then 中设置状态以便我们只设置一次状态会有帮助吗?
    • @ShubhamJawandhiya 是的,我就是这样做的(实际上我只是添加了一个 answer 这样做)。但是,我使用 Promise.all 而不是 axios.all,因为 axios.all 已被弃用。
    • 好的。我不知道弃用。谢谢(你的)信息。 @A.Copland 请参考尼克的答案以了解正确的方法。
    【解决方案3】:

    试试 setPokemon([...pokemon , response.data] )

    【讨论】:

      【解决方案4】:

      你应该使用

      setPokemon (pokemon.concat(response.data)
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2021-09-09
        • 2019-06-16
        • 2021-05-31
        • 2020-05-10
        • 1970-01-01
        • 2023-01-14
        • 1970-01-01
        • 2020-08-09
        相关资源
        最近更新 更多