如何在 useEffect 中等待 setState 直到渲染?
How to wait for setState in useEffect until render?
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() 中使用状态,但它似乎没有等待数据集。
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 设置的。
如何等待数据集?
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: [] });
一般来说,它会设置状态 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>
)
另一方面,您可以使用 optional chaining 来避免使用 undefined
值(即 item.tags
)中的 map
,正确的方法是替换 item.tags.map
到 item.tags?.map
.
最初,item
是空的 JSON ({}
)。您应该使用 optional chaining operator(?.
) 轻松摆脱 null 或未定义的异常。
return (
<div>
{item?.tags?.map((tag, index) => {
return <Chip label={tag} key={index} />
})}
</div>
)
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() 中使用状态,但它似乎没有等待数据集。
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 设置的。
如何等待数据集?
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: [] });
一般来说,它会设置状态 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>
)
另一方面,您可以使用 optional chaining 来避免使用 undefined
值(即 item.tags
)中的 map
,正确的方法是替换 item.tags.map
到 item.tags?.map
.
最初,item
是空的 JSON ({}
)。您应该使用 optional chaining operator(?.
) 轻松摆脱 null 或未定义的异常。
return (
<div>
{item?.tags?.map((tag, index) => {
return <Chip label={tag} key={index} />
})}
</div>
)