如何在React中用reduce与Promise.all实现多组数据高效展示
Hey Dave, great call wanting to clean up this code—duplicating the TopAnime rendering isn't ideal, and you're right that reduce can help here. Let's walk through fixing your reduce usage first, then refactor the component to reuse that TopAnime component properly.
First: Fix the reduce Usage
Your current reduce is trying to add arrays together with acc + anime, which converts everything to messy strings (not what we want!). Instead, we need to either concatenate the top arrays from each API response, or use reduce to build a structured list of categories (since you want to display TV and Airing separately with their own headers).
For category-structured data (perfect for your use case), here's how to use reduce correctly:
const topCategories = data.reduce((acc, response, index) => { // Match the order of your API calls to the headers you want const sectionTitles = ["Top TV", "Top Airing"]; acc.push({ title: sectionTitles[index], items: response.top.slice(0, 6) // Grab top 6 here to avoid repeating it later }); return acc; }, []);
Second: Refactor State & Data Fetching
Let's update your component state to hold this structured category array instead of two separate arrays. We'll also simplify the getData method by leaning fully into async/await (no more nested then calls!):
class HomePage extends Component { // New state structure: holds an array of category objects state = { topAnimeCategories: [] } async getData() { const api = "https://api.jikan.moe/v3"; const urls = [ `${api}/top/anime/1/tv`, `${api}/top/anime/1/airing` ]; try { // Fetch all API responses in parallel const responses = await Promise.all(urls.map(url => fetch(url))); // Convert all responses to JSON in parallel const data = await Promise.all(responses.map(resp => resp.json())); // Build our category array with reduce const topCategories = data.reduce((acc, response, index) => { const sectionTitles = ["Top TV", "Top Airing"]; acc.push({ title: sectionTitles[index], items: response.top.slice(0, 6) }); return acc; }, []); this.setState({ topAnimeCategories: topCategories }); } catch (err) { console.error("There was an error fetching anime data:", err); } } componentDidMount() { this.getData(); }
Third: Reuse the TopAnime Component (No More Duplication!)
Now we can loop through our topAnimeCategories state to render each section once. Also, note we replaced splice(0,6) with slice(0,6)—splice modifies the original array (which can cause unexpected state bugs), while slice safely returns a new array.
render() { return ( <HomeWrapper> {this.state.topAnimeCategories.map((category) => ( <div key={category.title}> <h2>{category.title}</h2> <TopAnime> {category.items.map((ani) => ( <div key={ani.mal_id}> <img src={ani.image_url} alt={`${ani.title} poster`} /> <h3>{ani.title}</h3> </div> ))} </TopAnime> </div> ))} </HomeWrapper> ) } }
Bonus: Scalability Win
If you ever want to add more sections (like Top Movies or Top OVAs), you just need to:
- Add a new URL to the
urlsarray - Add a corresponding title to the
sectionTitlesarray
No need to duplicate any rendering code—yourmaploop will handle the rest automatically.
内容的提问来源于stack exchange,提问作者Dave.Q

