选择下拉菜单时清除状态
Clear state on selecting dropdown
对 React 完全陌生,我正在构建一个基于 Pokeapi 的小型 Pokedex。我能够获取口袋妖怪及其相应的数据,并成功渲染它。
我有一个下拉菜单,用户可以在其中 select 口袋妖怪范围(开始和结束编号)并且口袋妖怪列表应该会自动更新。问题是,以前的范围没有被清除,当前范围与以前的范围相加。但是当我允许一个范围内的所有神奇宝贝完全加载并且 select 第二个范围时,我不会遇到这个错误。在范围之间快速切换会导致此错误。下面的代码片段。
问题是当我 select 一个下拉项时 allPokemons 被设置为空,但渲染中没有反映出来
展示我的问题的 GIF:Pokedex error
Codesandbox link : here
州:
constructor(props) {
super(props);
this.state = {
allPokemons: [],
limit: 151,
offset: 0,
regions: [
{
name: "Kanto",
limit: 151,
offset: 0,
},
{
name: "Johto",
limit: 100,
offset: 151,
},
]
}
}
正在获取宝可梦列表和相应的数据:
getAllPokemons = async () => {
const response = await axios.get(`https://pokeapi.co/api/v2/pokemon?limit=${this.state.limit}&offset=${this.state.offset}`).catch((err) => console.log("Error:", err));
this.getPokemonData(response.data.results);
}
getPokemonData = async (result) => {
this.setState({
allPokemons : [],
})
var response;
for (var i = 0; i < result.length; i++) {
response = await axios.get(`https://pokeapi.co/api/v2/pokemon/${result[i].name}`).catch((err) => console.log("Error:", err));
this.setState({
allPokemons: [...this.state.allPokemons,response.data],
})
}
}
下拉手柄更改功能
handleChangeRegions = (event) => {
this.setState({
allPokemons : [],
})
for (var i = 0; i < this.state.regions.length; i++) {
if (this.state.regions[i].name === event.target.value) {
this.setState({
limit : this.state.regions[i].limit,
offset : this.state.regions[i].offset,
allPokemons : [],
},()=>{
this.getAllPokemons();
})
break;
}
}
}
渲染组件
Object.keys(this.state.allPokemons).map((item, index) =>
<Pokemon
key={index}
id={this.state.allPokemons[item].id}
name={this.state.allPokemons[item].name}
type={this.state.allPokemons[item].types}
/>
)
下拉组件
<select value={this.state.valueregion} onChange={this.handleChangeRegions}>
{this.state.regions.map((region) => (
<option value={region.name}>{region.name} ({region.offset + 1}-{region.limit + region.offset})</option>
))}
</select>
我需要知道这个问题的原因,非常感谢解决方案!
还有一些注意事项,
- 在你命名的
Render Component
中,将键设置为数组的索引是一个非常糟糕的做法,这将导致呈现的项目出现问题,特别是 onclick 处理程序等等,使用从 api 响应返回的唯一 ID,而不是当您在 id 中使用它时(不确定您为什么使用该 id,但您需要设置密钥),更多信息 here
- 您经常使用
setState
,在 handleChangeRegions
中您重置了 allPokemons
键的状态,然后您使用下拉列表中的值再次设置它,同时您可以通过从下拉到下一个函数的值,这将执行 api 调用,你只需要在屏幕上有一些变化时使用状态,一个变化的变量应该导致你的组件重新渲染
- 此外,您每次都使用新响应设置状态,导致不必要的重新渲染,只需将 api 响应中的内容保存在数组中,然后将其传递给状态请求已通过
这可能不是解决方案,但它至少应该给您一些提示以缩小问题范围。如果您可以在代码沙箱中重新生成代码,这将非常有助于找出问题,而不是完整代码,但您可以替换 Pokemon 组件,例如显示 id 以方便我们调试代码。
编辑
由于问题现已更新并且原始问题已解决,我检查了您的沙箱,您正在发出单独的请求,每个请求都在等待之前的请求
for (var i = 0; i < result.length; i++) {
response = await axios
.get(`https://pokeapi.co/api/v2/pokemon/${result[i].name}`)
.catch((err) => console.log("Error:", err));
pokemonArr.push(response.data);
}
这就是你的罪魁祸首,每个请求都被发送,然后你正在等待该请求为每个口袋妖怪处理。你基本上发送了 200 个请求,但不是并行而是顺序发送,因为你正在使用异步等待.
用以下内容替换该部分
await Promise.all(
result.map(pokemonItem => {
return axios.get(`https://pokeapi.co/api/v2/pokemon/${pokemonItem.name}`)
.then(result => {
pokemonArr.push(result.data);
});
})
);
this.setState({
allPokemons:pokemonArr
})
Promise.all()
将所有请求一起发送,并将它们的响应与之前的 await
语句一起处理,只等到所有请求完成后再设置状态,瞧!工作起来很有魅力。
这里还有一些评论:
- 通常您在
componentDidMount()
生命周期挂钩中而不是在 comopnentWillMount()
中发送 api 调用
- 但我想你已经知道,发送 200 个请求来访问每个口袋妖怪的数据并将它们显示在列表中是后端服务的糟糕设计..我知道这不是你的,而是这样的案例你们在现实生活中通常不会见面。
你现在唯一可能觉得奇怪的是你的列表不会被排序..因为很多请求是并行发送的,我们不知道哪个请求会先被处理..一个简单的排序函数就可以解决这个问题,你可以找到一个 here
我已经用有效的解决方案更新了你的代码here祝你黑客愉快,欢迎来到 SO!
对 React 完全陌生,我正在构建一个基于 Pokeapi 的小型 Pokedex。我能够获取口袋妖怪及其相应的数据,并成功渲染它。
我有一个下拉菜单,用户可以在其中 select 口袋妖怪范围(开始和结束编号)并且口袋妖怪列表应该会自动更新。问题是,以前的范围没有被清除,当前范围与以前的范围相加。但是当我允许一个范围内的所有神奇宝贝完全加载并且 select 第二个范围时,我不会遇到这个错误。在范围之间快速切换会导致此错误。下面的代码片段。
问题是当我 select 一个下拉项时 allPokemons 被设置为空,但渲染中没有反映出来
展示我的问题的 GIF:Pokedex error
Codesandbox link : here
州:
constructor(props) {
super(props);
this.state = {
allPokemons: [],
limit: 151,
offset: 0,
regions: [
{
name: "Kanto",
limit: 151,
offset: 0,
},
{
name: "Johto",
limit: 100,
offset: 151,
},
]
}
}
正在获取宝可梦列表和相应的数据:
getAllPokemons = async () => {
const response = await axios.get(`https://pokeapi.co/api/v2/pokemon?limit=${this.state.limit}&offset=${this.state.offset}`).catch((err) => console.log("Error:", err));
this.getPokemonData(response.data.results);
}
getPokemonData = async (result) => {
this.setState({
allPokemons : [],
})
var response;
for (var i = 0; i < result.length; i++) {
response = await axios.get(`https://pokeapi.co/api/v2/pokemon/${result[i].name}`).catch((err) => console.log("Error:", err));
this.setState({
allPokemons: [...this.state.allPokemons,response.data],
})
}
}
下拉手柄更改功能
handleChangeRegions = (event) => {
this.setState({
allPokemons : [],
})
for (var i = 0; i < this.state.regions.length; i++) {
if (this.state.regions[i].name === event.target.value) {
this.setState({
limit : this.state.regions[i].limit,
offset : this.state.regions[i].offset,
allPokemons : [],
},()=>{
this.getAllPokemons();
})
break;
}
}
}
渲染组件
Object.keys(this.state.allPokemons).map((item, index) =>
<Pokemon
key={index}
id={this.state.allPokemons[item].id}
name={this.state.allPokemons[item].name}
type={this.state.allPokemons[item].types}
/>
)
下拉组件
<select value={this.state.valueregion} onChange={this.handleChangeRegions}>
{this.state.regions.map((region) => (
<option value={region.name}>{region.name} ({region.offset + 1}-{region.limit + region.offset})</option>
))}
</select>
我需要知道这个问题的原因,非常感谢解决方案!
还有一些注意事项,
- 在你命名的
Render Component
中,将键设置为数组的索引是一个非常糟糕的做法,这将导致呈现的项目出现问题,特别是 onclick 处理程序等等,使用从 api 响应返回的唯一 ID,而不是当您在 id 中使用它时(不确定您为什么使用该 id,但您需要设置密钥),更多信息 here - 您经常使用
setState
,在handleChangeRegions
中您重置了allPokemons
键的状态,然后您使用下拉列表中的值再次设置它,同时您可以通过从下拉到下一个函数的值,这将执行 api 调用,你只需要在屏幕上有一些变化时使用状态,一个变化的变量应该导致你的组件重新渲染 - 此外,您每次都使用新响应设置状态,导致不必要的重新渲染,只需将 api 响应中的内容保存在数组中,然后将其传递给状态请求已通过
这可能不是解决方案,但它至少应该给您一些提示以缩小问题范围。如果您可以在代码沙箱中重新生成代码,这将非常有助于找出问题,而不是完整代码,但您可以替换 Pokemon 组件,例如显示 id 以方便我们调试代码。
编辑
由于问题现已更新并且原始问题已解决,我检查了您的沙箱,您正在发出单独的请求,每个请求都在等待之前的请求
for (var i = 0; i < result.length; i++) {
response = await axios
.get(`https://pokeapi.co/api/v2/pokemon/${result[i].name}`)
.catch((err) => console.log("Error:", err));
pokemonArr.push(response.data);
}
这就是你的罪魁祸首,每个请求都被发送,然后你正在等待该请求为每个口袋妖怪处理。你基本上发送了 200 个请求,但不是并行而是顺序发送,因为你正在使用异步等待.
用以下内容替换该部分
await Promise.all(
result.map(pokemonItem => {
return axios.get(`https://pokeapi.co/api/v2/pokemon/${pokemonItem.name}`)
.then(result => {
pokemonArr.push(result.data);
});
})
);
this.setState({
allPokemons:pokemonArr
})
Promise.all()
将所有请求一起发送,并将它们的响应与之前的 await
语句一起处理,只等到所有请求完成后再设置状态,瞧!工作起来很有魅力。
这里还有一些评论:
- 通常您在
componentDidMount()
生命周期挂钩中而不是在comopnentWillMount()
中发送 api 调用
- 但我想你已经知道,发送 200 个请求来访问每个口袋妖怪的数据并将它们显示在列表中是后端服务的糟糕设计..我知道这不是你的,而是这样的案例你们在现实生活中通常不会见面。
你现在唯一可能觉得奇怪的是你的列表不会被排序..因为很多请求是并行发送的,我们不知道哪个请求会先被处理..一个简单的排序函数就可以解决这个问题,你可以找到一个 here
我已经用有效的解决方案更新了你的代码here祝你黑客愉快,欢迎来到 SO!