I'm trying to build a loading component so that my app doesn't try and display my API data before it has loaded, causing a fatal error. Read a lot about componentWillMount function but this seems to have been deprecated. Trying to use setState to no avail.
import { useEffect, useState } from "react";
import Marquee from "react-fast-marquee";
const News = () => {
const [data, setData] = useState({});
const [loading, setLoading] = useState(false);
useEffect(() => {
setLoading(true);
const getNewsFromApi = async () => {
const response = await fetch(
"API_KEY_HERE"
);
const responseJson = await response.json();
console.log("json", responseJson);
setData(responseJson);
};
setLoading(false);
setInterval(getNewsFromApi, 1000)
}, []);
if (loading) {
return <h1> Data is loading...</h1>
}
return (
<div >
<Marquee gradientColor="" speed="120">
<h1>{data?.articles[0].title} - </h1>
<h1>{data?.articles[1].title} - </h1>
<h1>{data?.articles[2].title} - </h1>
<h1>{data?.articles[3].title} - </h1>
<h1>{data?.articles[4].title} - </h1>
<h1>{data?.articles[5].title} - </h1>
<h1>{data?.articles[6].title} - </h1>
<h1>{data?.articles[7].title} - </h1>
<h1>{data?.articles[8].title} - </h1>
</Marquee>
</div>
)
}
export default News
CodePudding user response:
useEffect hook runs after the render (see docs). So what's going to happen:
- You initialize
loadingasfalse(by callinguseState(false)). - Component renders first time with
loading=falseso it skips loading placeholder and tries to immediately render articles list which is{}. It meansdata?.articlesisundefined.
I would do the following changes:
- Change your state declaration:
const [loading, setLoading] = useState(true);
This will make component to render loading placeholder first time. 2. Using state as an array of articles instead of object that is returned from your API also makes sense (as @Bru No suggested). But you'll need to make changes in your effect - something like:
setData(responseJson.articles);
CodePudding user response:
you start read infos from state before the api response i suggest to use this solution
import { useEffect, useState } from "react";
import Marquee from "react-fast-marquee";
const News = () => {
const [data, setData] = useState([]);
const [loading, setLoading] = useState(false);
useEffect(() => {
setLoading(true);
(async function getNewsFromApi(){
const response = await fetch(
"API_KEY_HERE"
);
const responseJson = await response.json();
console.log("json", responseJson);
setData(responseJson);
})();
setLoading(false);
}, []);
if (loading) {
return <h1> Data is loading...</h1>
}
return (
<div >
<Marquee gradientColor="" speed="120">
{data.length > 0 && data.articles.map((item)=> <h1>{item.title} </h1>)}
</Marquee>
</div>
)
}
export default News
CodePudding user response:
since I can't comment yet, ill ask here. can you show us your expected response from the API? also, I think it's better to set your data on an array so you can map it instead of displaying it one by one.
