React 和 PokéAPI:通过多个对象数组映射以获取神奇宝贝信息只会给我一个神奇宝贝的信息



我正在做一个个人项目来练习 React,这是一个 Pokédex 应用程序。

我在这里同时使用 React 和 PokéAPI。我将在此问题的底部显示代码片段。

我不得不做两个 API 调用,因为第一个是关于获取神奇宝贝的名称,第二个是检索更多信息(需要神奇宝贝的名称)。

挑战在于,我正在获取有关每个神奇宝贝的信息对象,并且我需要数组,以便我可以在JSX渲染中映射我需要的内容。因此,我使用了 Object.values 方法,这不可避免地导致我拥有一个可以迭代的对象数组,这很棒!

但是,现在当我映射所有我想要的对象时,我只会获得最新的神奇宝贝渲染(即假设我想查看有关 151 只神奇宝贝的信息,我只能在屏幕上看到 Mew 和"151"[这是我询问姓名和 ID])。

在下面的代码片段中,我只是将限制更改为 9 个神奇宝贝,这样我就不需要加载太多数据来查看我的代码是否正常工作:

import React, { Component } from 'react';
import './App.css';
import PokemonCard from './PokemonCard';
import axios from 'axios';
class App extends Component {
constructor() {
super();
this.state = {
pokemonInfo:[],
result: [],
pokemonName: "",
pokemonID: [],
introRegion: "",
primaryType: "",
secondaryType: "",
}
}
componentDidMount() {
axios({
method:'GET',
url: `https://pokeapi.co/api/v2/pokemon/?limit=9`,
dataResponse: 'json',
})
.then((dataOne) => {
console.log(dataOne.data.results)
this.setState({
result: dataOne.data.results
})
this.state.result.map(async(fetchInfo) => {
return axios({
method: 'GET',
url: `${fetchInfo.url}`,
dataResponse: 'json',
})
.then( (dataTwo) =>{
const dataTwoArray = Object.values(dataTwo)
console.log(dataTwoArray)
this.setState({
pokemonInfo: [dataTwoArray]
})
});
})
});
}
render() {
return (
<div className="App">
<h1>Pokédex!</h1>
{this.state.pokemonInfo.map((getInfo) => {
return (
<>
<h3>{getInfo[0].id}</h3>
<h3>{getInfo[0].name}</h3>
</>
)
})}
{/* <PokemonCard /> */}

</div>
);
}
}
export default App;

我想看看我一直在要求的神奇宝贝的所有名字和ID。 任何帮助将不胜感激!

因此,您在这里的尝试存在几个问题。其中一些与您的问题有关,而另一些则不那么相关。

回到那些与你的问题相关的问题:你似乎对setState做什么有误解。该函数setState采用新状态的部分版本并创建下一个状态 - 然后在状态更新后重新呈现组件。关键是它是状态的部分视图,而不是对现有状态的修改。为什么这是相关的,我将在第 #2 点中详细说明。

1)首先,您调用setStateresult更新为API响应,然后立即尝试使用this.state.result- 这可能有效,但非常有问题。您不应该依赖于setState何时更新this.state。通常,您已经拥有传递的数据,因此只需使用它即可。在这种情况下,您应该映射dataOne.data.results而不是映射this.state.result。这种在setState后使用this.state可能会起作用,但这是一个等待发生的竞争条件噩梦,而且是非常糟糕的做法。现在打破它。

2)其次,也是我之前的观点。setState正在对下一个状态采取部分看法。因此,当您调用this.setState({ pokemonInfo: [dataTwoArray] })时,您不是说"将此新值添加到数组中pokemonInfo",而是在说"replace the current list of information with this new list of information"。我不太喜欢推荐这个,因为它回到了第一部分的问题,但您可以通过扩展现有数组和/或连接新数组来解决这个问题。this.setState({ pokemonInfo: this.state.pokemon.concat([dataTwoArray]) })或(更现代的)this.setState({ pokemonInfo: [...this.state.pokemonInfo, dataTwoArray] }).这两者都取决于先前对setState成功的调用,以便相应地捕获所有现有值,这是有问题的。

好的,所以问题是当您更新口袋妖怪信息时,您将其设置为数组中的单个值。在这样做时,您(通常)只能看到最后一个通过的(更准确地说,最后一个完成请求)。

现在,我说我对问题2的解决方案很糟糕,而且确实如此。由于您确实在构建一个承诺列表,并且您想等待它们全部完成,因此您应该使用更多承诺来做到这一点。

// inside the first promise's then
// We start by starting a request promise by mapping each pokemon
// data to a request. This results in pokePromises being an array
// of Promise objects.
const pokePromises = dataOne.data.results.map(pokemon => axios({
method: 'GET',
url: pokemon.url, // notice no need here to make it a string, it's already a string!
dataResponse: 'json',
}));
// Now wait for all the requests to finish before doing more, Promise.all
// creates a promise that will resolve when all given promises have been
// resolved. This doesn't handle errors though, we're assuming all success
// here.
Promise.all(pokePromises).then(allPokeData => {
// This takes each response object and converts into a an array 
// of values, just like you do already. We're just operating on
// all the results at one time instead of one result like you are.
const pokemonInfo = allPokeData.map(data => Object.values(data));
// Now that we have all the data we set the state, but we use
// an ES6 shorthand that expands to `{ pokemonInfo: pokemonInfo }`
this.setState({ pokemonInfo });
});

(代码沙盒:https://codesandbox.io/s/cocky-edison-kfp85)

另一种可能会减少您所做的更改量的选项是将更新函数传递给setState而不是对象。

这将需要您的最终更新pokemonInfo如下所示:

// This way of updating state guarantees we get the current version 
// of state (in the updater function) when this update is being
// applied and can ensure us that our new pokemonInfo will truly contain
// the previous and new values as expected.
this.setState(state => ({
...state, // merge in previous state
pokemonInfo: [...state.pokemonInfo, dataTwoArray],
}));

(代码沙盒:https://codesandbox.io/s/silly-noether-5z53f)

代码的其他问题是名称。像dataOne一样,dataTwodataTwoArray在阅读代码的上下文中没有任何意义。它们现在可能对你有意义,但当你回来看这个项目时,它们不会在一个月或更长时间内发生。对于阅读您的代码的任何其他人来说,它们也没有任何意义。您应该努力使用有意义且可读的名称。所以dataOne应该是pokeListResponse,也许dataTwo应该是pokeDatapokemonResponsefetchInfo是神奇宝贝,所以叫它pokemon,对吧?还要注意您的数据类型。fetchInfo.url是一个字符串,插入到一个字符串是毫无意义和多余的。还要处理缩进和间距。它可能是StackOverflow的格式,或者您使用的任何编辑器,但它无处不在。间距是一件好事,它为代码增加了秩序和清洁度。对于可读性非常必要。

无论如何,希望您发现其中一些和/或全部对您有所帮助。努力了解 React 组件生命周期的工作原理以及更新状态如何与重新渲染相关联 - 以及对 JS 的更深入理解。至少从这个代码示例来看,您似乎有一些表面知识。不断成长,扩大您的知识财富!

相关内容

最新更新