So I have an app with friends and events, where each friends has specific events.
I just want to retrieve every single event from every single friend.
I have an 'event' state const [events, setEvents] = useState([]);
Then here is the problematic useEffect snippet:
useEffect(() => {
getFriendsIds()
.then(idsArray => {
Promise.all(idsArray.map(id => {
getUserEvents(id)
.then(evs => {
if (evs.length > 0) {
for (let i=0; i<evs.length; i ) {
setEvents([...events, evs[i]]);
}
}
})
}))
})
}, [])
This should store the aforementioned events in the 'events' state variable.
However, when I try to render them with {events.map(ev => renderItem(ev))}, I only get the very last event of the last friend in the loop (It is guaranteed that renderItem works fine).
I would love an explanation of how this works, and if possible an alternative algorithm that actually achieves what I aim to do.
Thanks
CodePudding user response:
Actually, this happens because your useEffect() doesn't have the events state in its dependency array.
useEffect(() => {
...
}, [events])
However event this solution will not work properly because the setState() function works asynchronously.
You have to use state updater function in this case:
useEffect(() => {
...
setEvents((prevEvents) => [...prevEvents, evs[i]]);
...
}, []);
Read more about dependency array and state updater in the official documentation
CodePudding user response:
Try passing a function to setEvents and remove the for loop.
if (evs.length > 0) {
setEvents((prev) => ([...prev, ...evs]));
}
CodePudding user response:
Move the setState out of the loop -
useEffect(async () => {
const ids = await getFriendsIds()
const events = await Promise.all(ids.map(getUserEvents)))
setEvents(events.flat()) // replace
}, [])
If you want to append events to the existing events state, you can use a functional update -
useEffect(async () => {
const ids = await getFriendsIds()
const events = await Promise.all(ids.map(getUserEvents)))
setEvents(r => [...r, ...events.flat()]) // append
}, [])
You can use .flat instead of checking for .length as it will remove empty arrays for you automatically -
const a = [["event1", "event2"], [], ["event3"], ["event4"], []]
console.log(a.flat())
// ["event1", "event2", "event3", "event4" ]
