useEffect 不等待 Promise.all?

useEffect doesn't wait Promise.all?

我想为数组中作为道具传递的每个对象获取数据(rootComments)并将数据添加到对象属性:

useEffect(() => {
  rootComments.map(rootComment => {
    if (rootComment.kids) {
      rootComment.kidsText = []
      fetchKidsText(rootComment.kids)
    }
  })
  console.log(rootComments)
  rootComments.map(comment => console.log(Object.values(comment)))
}, [rootComments])

const fetchKidsText = async (ids) => {
  await Promise.all(
    ids.map(id => fetch(`https://hacker-news.firebaseio.com/v0/item/${id}.json?print=pretty`).then(response => response.json()).then(result => {
      rootComments.map(comment => {
        if (comment.id === result.parent) {
          comment.kidsText = comment.kidsText.concat(result.text)
        }
      })
    }))
  );
}

它似乎不起作用,我无法呈现 rootComments.map(comment => comment.kidsText) 并且我无法将其记录为 rootComments.map(comment => console.log(Object.values(comment)))(就像没有新的 kidsText 属性) 或 rootComments.map(comment => console.log(comment.kidsText))(returns 空数组)。 但。当我记录 console.log(rootComments) 它 returns 具有新 kidsText 属性.

的对象数组

我的第一个想法是 useEffect() 不会等待 rootComments 获取另一个组件内部,但这似乎不是问题,所以可能是 fetchKidsText() 内部的东西或者它与 useEffect()?

交互的方式

此代码有几处错误:

  • 您在未使用 setState 的情况下改变 rootComments 对象(React 无法从中更新)
  • 您使用 map 进行迭代但丢弃了结果
  • .thenasync / await 函数中的混合
  • 您没有 return Promise.all 的值,而是 await

.map 不是 for 循环,请使用 .forEachfor..of 循环

至于你的实际问题,我的第一点可能是主要问题,请显示更多代码以便我们确认

edit suggested alternative code:

useEffect(() => {
  // get all the kids id, this create a single big array with every kids:
  const allKids = rootComments.flatMap(rootComment => rootComment.kids || [])

  // use a controller too cancel fetch request if component is unmounted
  // otherwhise, trying to setState if the component is unmounted will
  // mess react up:
  const controller = new AbortController()

  // we fetch all the kids at once instead of doing this in a map like before
  fetchKidsText(allKids, controller.signal)
    .then(setRootComment) // note that i'm using `.then` here,
    // because we can't await in hooks we must return a callback or undefined.
    .catch(err => {
      if (err.name === 'AbortError') return // we can safely ignore abort error
      // ? handle error here
    })
  return () => controller.abort()
}, [rootComments])

const fetchKidsText = async (ids, signal) => {
  // first we call the api and get everything a big result
  const results = await Promise.all(ids.map(async id => {
    const url = `https://hacker-news.firebaseio.com/v0/item/${id}.json?print=pretty`
    const res = await fetch(url, { signal })
    return res.json()
  }))

  // now what you were trying to do was merge every children text comment
  // since we got them all, just one filter should work:
  return rootComments.map(comment => {
    const children = results.filter(r => r.parent === comment.id)
    const kidsText = children.map(r => r.text)
    return { ...comments, kidsText }
  })
}

不要按原样使用此代码,我没有测试它只是为了解释我将如何尝试通过评论来解决您的问题来解释我的共鸣。

祝你好运!