React多接口并行请求时console.log先输出undefined后返回目标响应的问题咨询
movieDetails[0] log undefined initially when using Promise.all in React? Problem Description
I'm working on a React component where I use Promise.all to fetch multiple movie-related APIs in parallel, storing the returned data in the movieDetails state. However, when I run console.log(movieDetails[0]), it outputs undefined on the first render, only showing the expected data after the API requests complete. Here's my component code:
import React, { useEffect, useState } from "react"; import { endpoint, apiKey } from "../api"; import Container from "../components/layouts/Container"; export default function Movie({ route }) { const { movieId } = route.params; const [movieDetails, setMovieDetails] = useState([]); const [isLoading, setIsLoading] = useState(false); const urls = [ `${endpoint}/movie/${movieId}?api_key=${apiKey}`, `${endpoint}/movie/${movieId}/credits?api_key=${apiKey}`, `${endpoint}/movie/${movieId}/images?api_key=${apiKey}`, `${endpoint}/movie/${movieId}/reviews?api_key=${apiKey}`, `${endpoint}/movie/${movieId}/similar?api_key=${apiKey}`, ]; useEffect(() => { const fetchData = () => { setIsLoading(true); Promise.all( urls.map((url) => { return fetch(url); }) ) .then((response) => { return Promise.all(response.map((res) => res.json())); }) .then((data) => { setMovieDetails(data); setIsLoading(false); }) .catch((err) => { console.log(err); }); }; fetchData(); }, []); console.log(movieDetails[0]); }
Root Cause
This happens because React state updates are asynchronous, and here's the breakdown of the component's lifecycle:
- On the initial mount,
movieDetailsis set to its default value: an empty array[]. When the component renders the first time,movieDetails[0]isundefined, so that's what gets logged. - The
useEffecthook runs after the first render, kicking off the asynchronous API requests. This process doesn't block the component from rendering. - Once all requests complete and
setMovieDetails(data)is called, React schedules a re-render of the component. On this re-render,movieDetailsholds the API response data, soconsole.log(movieDetails[0])outputs the expected result.
In short, the first log runs before your API requests finish, so the state still has its initial empty value.
Solutions
1. Guard Against Empty State with Conditionals
Before accessing movieDetails[0], check if the state has data to avoid undefined:
// Only log when data exists if (movieDetails.length > 0) { console.log(movieDetails[0]); }
You can also extend this to conditionally render content in your component, which improves user experience by handling loading and empty states:
if (isLoading) { return <div>Loading movie details...</div>; } if (movieDetails.length === 0) { return <div>No movie data available</div>; } // Render your movie content once data is ready return ( <Container> <h1>{movieDetails[0].title}</h1> {/* Add other movie details here */} </Container> );
2. Use Optional Chaining for Safe Property Access
If you want to avoid errors when accessing properties on movieDetails[0], use the ES6 optional chain operator ?. — it will return undefined instead of throwing an error if the parent value doesn't exist:
// Safely access a property without breaking the app console.log(movieDetails[0]?.title);
3. Work with the API Response Directly (Instead of Waiting for State)
When your Promise.all resolves, you can work with the data variable directly instead of relying on the updated state (since state updates are async). This is useful if you need to process the data immediately after fetching:
.then((data) => { setMovieDetails(data); setIsLoading(false); // Log the data directly here — no wait for state update needed console.log(data[0]); })
4. Fix the useEffect Dependency Array
Right now, your useEffect only runs once on mount, but if movieId changes (e.g., when navigating to a different movie), it won't re-fetch data. Add movieId to the dependency array to ensure the component fetches fresh data whenever the movie ID changes:
useEffect(() => { const fetchData = () => { // ... existing fetch logic }; fetchData(); }, [movieId]); // Add movieId as a dependency
内容的提问来源于stack exchange,提问作者idan cohen

