React状态数组已更新,但仅渲染数组中第一个对象问题求助
Hey there! I see the issue you're facing—only the first Fire-type Pokémon is rendering, right? This boils down to how you're handling asynchronous requests and state updates in React. Let's walk through what's going wrong and how to fix it:
核心问题
You’re updating the component state immediately after each individual Pokémon detail request finishes. Since these AJAX calls are asynchronous, they complete in an unpredictable order, and each setState triggers a re-render before all data is ready. Additionally, React’s setState is asynchronous under the hood, so frequent, scattered state updates can lead to incomplete or overwritten state values.
Another small issue: you added a key prop to the <table> inside your PokemonRow component, which is unnecessary. The key should only be applied to the top-level element being looped over (the <PokemonRow> itself in your App component).
Fixed Code
Updated App Component
We’ll convert each AJAX request to a Promise, wait for all requests to finish with Promise.all, then update the state once with all the Pokémon data:
import React, { Component } from 'react'; import './App.css'; import PokemonRow from './PokemonRow.js' class App extends Component { constructor(props) { super(props); this.state = {rows: []}; this.performSearch() } performSearch() { const urlString = "http://pokeapi.salestock.net/api/v2/type/fire/"; // Use native fetch instead of jQuery for cleaner React practice fetch(urlString) .then(response => response.json()) .then(searchResults => { console.log("Fetched Data Successfully"); console.log(searchResults); const pokemonOfType = searchResults.pokemon; // Convert each detail request into a Promise const pokemonDetailPromises = pokemonOfType.map(pokemon => { return fetch(pokemon.pokemon.url).then(res => res.json()); }); // Wait for all detail requests to complete Promise.all(pokemonDetailPromises) .then(fullPokemonList => { // Generate all PokemonRow components at once const pokemonRows = fullPokemonList.map(pokemon => ( <PokemonRow key={pokemon.id} pokemon={pokemon} /> )); // Update state once with all data this.setState({ rows: pokemonRows }); console.log(`Total Fire-type Pokémon loaded: ${pokemonRows.length}`); }) .catch(err => console.error("Failed to fetch Pokémon details:", err)); }) .catch(err => console.error("Failed to fetch type data:", err)); } render() { return ( <div className="App"> <table className="titleBar"> {/* Fixed typo: <tb> → <tbody> */} <tbody> <tr> <td> <img width="50" src="pokemon.png" alt="Pokemon logo"/> </td> <td> Pokemon Information </td> </tr> </tbody> </table> <input className="searchBar" placeholder="Enter Pokemon Name" /> {this.state.rows} </div> ); } } export default App;
Updated PokemonRow Component (Removed Unnecessary Key)
import React from 'react' class PokemonRow extends React.Component { render() { return ( <table> <tbody> <tr> <td> {this.props.pokemon.name} </td> </tr> <tr> <td> <img alt="pokemon_image" width="120" src={this.props.pokemon.sprites.front_default} /> </td> <td> <h3>Stats</h3> <p>Height: {this.props.pokemon.height} ft</p> <p>Weight: {this.props.pokemon.weight} lbs</p> </td> </tr> </tbody> </table> ); } } export default PokemonRow;
Why This Works
Promise.allfor Async Coordination: This ensures we don’t update state until every Pokémon’s detail data has been fetched, so we render all results at once instead of partial batches.- Native Fetch API: Eliminates the need for jQuery, aligning with modern React project practices.
- Single State Update: Only one
setStatecall triggers a single re-render, which is more efficient and avoids state inconsistencies. - Fixed HTML Typo: Corrected
<tb>to<tbody>for valid table structure.
With these changes, all Fire-type Pokémon should render correctly on your page!
内容的提问来源于stack exchange,提问作者khanvict

