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);
});
}, []);
i setStated like above. and I was trying to use the State in return(), but it seems it didn't wait for the data set.
return (
<div>
{item.tags.map((tag, index) => {
return <Chip label={tag} key={index} />
})}
</div>
)
because i got an error message like this : Uncaught TypeError: Cannot read properties of undefined (reading 'map'). Since i initialized 'item' just empty {object}, so it can't read 'item.tags', which is set by setState in useEffect.
How can i wait for the data set?
In generic, it would set a state isFetched
to determine if the data from api is ready or not. And when the isFetched
equal to true
, it means the item.tags
have value.
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>
)
On the other hand, you could use optional chaining to avoid using map
from an undefined
value (that is item.tags
), the right way is replace item.tags.map
to item.tags?.map
.