开发者问题收集

如何等待 useEffect 中的 setState 直到渲染?

2022-03-06
3003
let [item, setItem] = useState({});
  let [comments, setComments] = useState([]);
  useEffect(async () => {
    await axios
      .all([
        axios.get(`https://dummyapi.io/data/v1/post/${id}`, {
          headers: { "app-id": process.env.REACT_APP_API_KEY }
        }),
        axios.get(`https://dummyapi.io/data/v1/post/${id}/comment`, {
          headers: { "app-id": process.env.REACT_APP_API_KEY }
        })
      ])
      .then(
        axios.spread((detail, comment) => {
          setItem({ ...detail.data })
          setComments([...comment.data.data])
        })
      )
      .catch((detail_err, comment_err) => {
        console.error(detail_err);
        console.error(comment_err);
      });
  }, []);

我像上面一样设置了 State。 并且我试图在 return() 中使用 State,但似乎它没有等待数据集。

return (
          <div>
            {item.tags.map((tag, index) => {
              return <Chip label={tag} key={index} />
            })}
          </div>
)

因为我收到这样的错误消息:Uncaught TypeError:无法读取未定义的属性(读取“map”)。 由于我初始化的“item”只是空的 {object},所以它无法读取“item.tags”,这是由 useEffect 中的 setState 设置的。

我如何等待数据集?

3个回答

一般来说,它会设置一个状态 isFetched 来确定来自 api 的数据是否已准备就绪。当 isFetched 等于 true 时,表示 item.tags 具有值。

const [isFetched, setIsFetched] = useState(false);
useEffect(async () => {
  await axios.all(...).then(() => {
    ...
    ...
    setIsFetched(true);
  })
}, [])

// You could return null or an Loader component meaning the api is not ready
if (!isFetched) return null;
return (
      <div>
        {item.tags.map((tag, index) => {
          return <Chip label={tag} key={index} />
        })}
      </div>
)

另一方面,您可以使用 可选链 来避免使用来自 undefined 值(即 item.tags )的 map ,正确的方法是将 item.tags.map 替换为 item.tags?.map

bcjohn
2022-03-06
let [item, setItem] = useState({});

您的初始状态是一个空对象,并且始终会至少有一个渲染使用此初始状态。因此,您的代码需要能够在具有此状态时正常工作。例如,您可以在尝试使用 item.tags 之前检查它是否存在:

if (item.tags) {
  return (
    <div>
      {item.tags.map((tag, index) => {
        return <Chip label={tag} key={index] />
      })}
    </div>
  );
} else {
  return <div>Loading...</div>
}

或者,您可以更改初始状态,使其具有与加载完成后相同的形状:

let [item, setItem] = useState({ tags: [] });
Nicholas Tower
2022-03-06

最初, item 是一个空的 JSON ( {})。您应该使用 可选链接运算符 ( ?. ) 轻松摆脱 null 或未定义的异常。

return (
          <div>
            {item?.tags?.map((tag, index) => {
              return <Chip label={tag} key={index} />
            })}
          </div>
)
Amila Senadheera
2022-03-06