Home > Software engineering >  I am wondering why my useEffect data is empty on first render when I populate it on first render usi
I am wondering why my useEffect data is empty on first render when I populate it on first render usi

Time:08-24

I am creating an app using the REST countries API and I make an API call on the first render using useEffect hook and populate my useState array with the data that I need. When I try accessing this data in the return statement for the component it reads undefined. I even tried adding a ternary operator to first check if the data is empty and if not output it but it still reads undefined but If I comment out the code and refresh the page the data become populated and can then uncomment the code for it to show and if I were to refresh the page again the data reads as undefined. I am extremely confused as to why this is happening so any help is much appreciated.

My js file:

const CountryInfo = () => {
    const [data, setData] = useState([{}])
    let countryName = window.location.pathname;

    countryName = countryName.replace('/', '');
    countryName = countryName.split(' ');
    countryName = countryName.join(' ');
    console.log(countryName)

    useEffect(() => {
       axios.get(`https://restcountries.com/v3.1/name/${countryName}`).then(response => {
        console.log(response);
        setData([{
            name: countryName,
            image: response.data[0].flags.png,
            nativeName: response.data[0].name.nativeName.nld,
            population: response.data[0].population,
            region: response.data[0].region,
            subRegion: response.data[0].subregion,
            capital: response.data[0].capital[0],
            borderCountries: response.data[0].borders,
            topLevelDomain: response.data[0].tld[0],
            currencies: response.data[0].currencies,
            languages: response.data[0].languages,
        }])
       }) 
    },[])

    console.log(data)

    return(
        <div className='country-info-content'>
            <Link to='/' className='info-button'>
                <button className='button-info'>BACK</button>
            </Link>
            <div className='data-info'>
                <img src={data[0].image} className='flag-img'/>
                <div className='country-name'>
                    <span className='name'>{data[0].name}</span>
                    <div className='info-description'>
                        <div>
                            <div>
                                <span>Native Name:</span> {data[0].nativeName.common}
                            </div>
                        {/* {data?
                            <div>
                                <div>
                                <span className='title'>Native Name:</span> {data.nativeName.common}
                                </div>
                                <div>
                                    <span className='title'>Population:</span> {data.population.toLocaleString(undefined)}
                                </div>
                                <div>
                                    <span className='title'>Region:</span> {data.region}
                                </div>
                                <div>
                                    <span className='title'>Sub Region:</span> {data.subRegion}
                                </div>
                                <div>
                                    <span className='title'>Capital:</span> {data.capital}
                                </div> 
                            </div>:null} */}
                            {/* {data ?
                                <div>
                                    <span>Native Name: </span>{data[0].nativeName.common}
                                </div>: null
                            } */}
                        </div>
                        <div className='right-info'>
                            <div>
                                <span className='title'>Top Level Domain:</span> {data.topLevelDomain}
                            </div>
                            <div>
                                <span className='title'>Currency:</span> {data.topLevelDomain}
                            </div>
                            <div>
                                <span className='title'>Languages:</span> {data.topLevelDomain}
                            </div>
                        </div>
                    </div>
                </div>
            </div>
        </div>
    )
}

export default CountryInfo;

Output to the web

CodePudding user response:

You have a data mapping error. You are setting your Data incorrectly when trying to set the native name in your data array.

you have:

setData([
{
    ...
    nativeName: response.data[0].name.nativeName.nld,
    ...
  }
]);

and return

                            <div>
                            <span className='title'>Native Name:</span> {data.nativeName.common}
                            </div>

But the data structure returned by the api looks different, eg:

data: Array(12)
  0: Object
    name: Object
      common: "Eswatini"
      official: "Kingdom of Eswatini"
      nativeName: Object
        eng: Object
        ssw: Object

Change it to this instead:

setData([
{
    ...
    nativeName: response.data[0].name.common,
    ...
  }
]);

and

              <div>
                <span>Native Name:</span> {data[0].nativeName}
              </div>

CodePudding user response:

useEffect will trigger AFTER your page is rendered for the first time, that's why data is undefined the first time component renders.

You'll need to handle it and the ternary operator is a common practice:

return data ? <div>...</div> : null;

or

return data ? <div>...</div> : <div>Loading...</div>;

UPDATE: The error message is triggered by reading common property of undefined. The only place where you do this is

data[0].nativeName.common

This means that data[0] exists, but nativeName is undefined. nativeName stores the value of response.data[0].name.nativeName.nld,. You should double check if data returned from the API has expected format and response.data[0].name.nativeName.nld is set.

UPDATE2: Like Zeno noticed in the comment, default value for data is [{}], and that will always evaluate to true in ternary operator.

Why not make the default undefined until data is fetched?

Also make sure you refer to the results as data[0] as this is where you store the response. Or simplify your code a bit by doing:

setData({
            name: countryName,
            image: response.data[0].flags.png,
            nativeName: response.data[0].name.nativeName.nld,
            population: response.data[0].population,
            region: response.data[0].region,
            subRegion: response.data[0].subregion,
            capital: response.data[0].capital[0],
            borderCountries: response.data[0].borders,
            topLevelDomain: response.data[0].tld[0],
            currencies: response.data[0].currencies,
            languages: response.data[0].languages,
        })

There's no need to store an array in there.

  • Related