I’m having trouble with React state updates when working with multiple API calls. I’m fetching a list of products first, then for each product I need to get the seller information and add it to my state array.
The issue is that when I use setSellers([...sellers, seller]) inside the loop, only the last seller gets added to the state instead of all of them. Both API calls return successfully but the state doesn’t update correctly.
Here’s my code:
const [sellers, setSellers] = useState([])
try {
const response = await fetchProducts(queryParams) // gets multiple products
if (response.status === 200) {
for (let product of response.data.products) {
const seller = await fetchSeller(product.data.sellerId)
if (seller.status === 200) {
setSellers([...sellers, seller]) // only last one is saved
}
}
}
} catch (err) {
console.log(err)
}
What’s causing this behavior and how can I fix it?
The issue you’re encountering is due to the asynchronous nature of state updates in React. Within your loop, each setSellers call references the initial stale state of sellers, which leads to only the last seller being saved to the state.
I’ve faced a similar challenge when I was building a feature that involved fetching data from APIs. The best approach here is to gather all the seller data in an array first and then call setSellers once with the complete array.
yeah, this closure trap gets everyone! way simpler solution - just collect all sellers in a regular array first, then set state once at the end. something like let tempSellers = [], push to that inside the loop, then setSellers(tempSellers) outside. completely avoids the stale closure mess.
This happens because React batches state updates and they’re asynchronous. When you call setSellers([...sellers, seller]) in the loop, sellers always points to that initial empty array - not the updated version from previous iterations. I hit this exact issue building a dashboard that fetched user details for multiple orders. Skip the spread operator with current state and use the functional update pattern instead: javascript const [sellers, setSellers] = useState([]) try { const response = await fetchProducts(queryParams) if (response.status === 200) { for (let product of response.data.products) { const seller = await fetchSeller(product.data.sellerId) if (seller.status === 200) { setSellers(prevSellers => [...prevSellers, seller]) } } } } catch (err) { console.log(err) } The key difference? Using prevSellers => [...prevSellers, seller] means you’re always working with the most recent state value. Each seller gets added properly this way.