简体   繁体   English

setState 循环似乎只在最后一次迭代中运行

[英]setState loop seems to only run on the last iteration

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([]);我有一个“事件” state const [events, setEvents] = useState([]);
Then here is the problematic useEffect snippet:然后是有问题的useEffect片段:

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.这应该将上述事件存储在“事件” state变量中。
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).但是,当我尝试使用{events.map(ev => renderItem(ev))}渲染它们时,我只得到循环中最后一个朋友的最后一个事件(保证 renderItem 工作正常)。
I would love an explanation of how this works, and if possible an alternative algorithm that actually achieves what I aim to do.我很想解释它是如何工作的,如果可能的话,还有一个替代算法可以真正实现我的目标。
Thanks谢谢

Actually, this happens because your useEffect() doesn't have the events state in its dependency array.实际上,这是因为您的useEffect()在其依赖数组中没有events state 。

useEffect(() => {
   ...
}, [events])

However event this solution will not work properly because the setState() function works asynchronously.但是,此解决方案将无法正常工作,因为setState() function 异步工作。

You have to use state updater function in this case:在这种情况下,您必须使用 state 更新程序 function :

useEffect(() => {
  ...
  setEvents((prevEvents) => [...prevEvents, evs[i]]);
  ...
}, []);

Read more about dependency array and state updater in the official documentation在官方文档中阅读有关dependency arraystate updater的更多信息

Try passing a function to setEvents and remove the for loop.尝试将 function 传递给 setEvents 并删除 for 循环。

if (evs.length > 0) {
    setEvents((prev) => ([...prev, ...evs]));
}

Move the setState out of the loop -setState移出循环 -

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 -如果要将 append events改为现有事件 state,可以使用功能更新 -

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 -您可以使用.flat而不是检查.length因为它会自动为您删除空的 arrays -

 const a = [["event1", "event2"], [], ["event3"], ["event4"], []] console.log(a.flat()) // ["event1", "event2", "event3", "event4" ]

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM