Reputation: 39
So I am creating an app that utilizes the REST countries API and I am trying to call to the API on the first render and from my understanding, in order to do this you have to use an empty array in the useEffect function as such
const LightMode = () => {
const data = useRef([])
useEffect(() =>{
axios.get('https://restcountries.com/v3.1/all').then(res=>{
res.data.forEach(country =>{
//console.log(country)
data.current.push({
name: country.name.common,
population: country.population,
region: country.region,
capital: country.capital,
image: country.coatOfArms.png
})
})
})
}, [])
console.log(data)
return(
<div>
<NavigationBar />
<div className='temp'>
<Card className='country-cards'>
<Card.Img variant='top' src={data[0].image}/>
<Card.Body>
<Card.Title></Card.Title>
</Card.Body>
</Card>
</div>
</div>
)
}
but when I run the app I get an error saying unable to read undefined so the first render technically never runs? I want to know why that is. I am still learning how first renders work so any help is much appreciated, also if there is any more information needed let me know.
Upvotes: 2
Views: 1686
Reputation: 865
At here, I use new_contries
to save country when i fillter country(name, population, region, capital, image)
. After that, i SetContries (new_countries)
const LightMode = () => {
const [countries, setCountries] = useState([]);
useEffect(() => {
axios.get("https://restcountries.com/v3.1/all").then((res) => {
let new_countries: any = [];
res.data.forEach((country: any) => {
new_countries = [...new_countries, {
name: country.name.common,
population: country.population,
region: country.region,
capital: country.capital,
image: country.coatOfArms.png,
}];
});
setCountries(new_countries);
});
}, []);
return (
<div>
{countries?.map((country) => (
<>
<div>{country["name"]}</div>
</>
))}
</div>
);
}
Upvotes: 0
Reputation: 697
use state
instead of ref
with async
API call.
import axios from "axios";
import { useEffect, useState } from "react";
const LightMode = () => {
const [country, setCountry] = useState([]);
useEffect(() => {
const getCountries = async () => {
const countries = await axios.get("https://restcountries.com/v3.1/all");
const filterCountries = countries.data.map((country) => {
return {
name: country.name.common,
population: country.population,
region: country.region,
capital: country.capital,
image: country.coatOfArms.png
};
});
setCountry(filterCountries);
};
getCountries();
}, []);
return (
<div>
<NavigationBar />
{country.map((country) => (
<>
<div className="temp">
<Card className="country-cards">
<Card.Img variant="top" src={country.image} />
<Card.Body>
<Card.Title></Card.Title>
</Card.Body>
</Card>
</div>
</>
))}
</div>
);
};
export default LightMode;
Upvotes: 0
Reputation: 504
You are combining two things:
To actually test out whether useEffect ran, you can:
Since your UI is dependent upon the response, it will be a good thing to add a:
useEffect is supposed to run on every render unless a dependency array is provided, which will ensure that it only runs if a dependency changes. By providing an empty array, you are limiting useEffect to run on JUST the first render.
Upvotes: 0
Reputation: 559
There are two issues at play here:
You are using Ref, which when updates will not trigger a re-render, which means once your REST call returns, the component will not re-render with your new data. Try using state instead of ref.
useEffect does run on the first render, but it is not blocking the render. Meaning, on the first render, useEffect is triggered, but it does not wait for the REST call to return before rendering the component. There are several methods to deal with it:
and many more...
Upvotes: 4
Reputation: 159
Try adding optional chaining to your code:
<Card.Img variant='top' src={data[0]?.image}/>
Upvotes: -1