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

IT技术 reactjs use-effect
2022-07-08 01:33:20
  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);
      });
  }, []);

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

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

因为我收到这样的错误消息:Uncaught TypeError: Cannot read properties of undefined (reading '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>
)

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

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

return (
          <div>
            {item?.tags?.map((tag, index) => {
              return <Chip label={tag} key={index} />
            })}
          </div>
)
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: [] });