Reputation: 17
Hi im trying so set String to data i have fetched. The problem is it is stuck in a promise and returns null before the state can be updated. I am lost as to how I can make sure the data is there before it accesses the data. Any help would be much appreciated. thanks
import React, {useState,useEffect} from 'react'
import {useParams} from 'react-router-dom'
import Header from '../components/header.js'
import{BsArrowLeft} from 'react-icons/bs'
const Country = () => {
const {countryName} = useParams();
const [country,setCountry] = useState(countryName);
const [details,setDetails] = useState(null);
const [string,setString] = useState(null);
useEffect( () => {
async function fetchData(){
const res = await fetch(`https://restcountries.com/v3.1/name/${country}`)
const data = await res.json()
setDetails(data)
setString(Object.keys(data.currencies).toString())
}
fetchData()
}, [country])
return (
<main className='h-screen flex flex-col items-center w-full overflow-x-hidden bg-verylightgrey dark:bg-verydarkblue overflow-scroll'>
<div className='flex flex-col items-center w-full'>
<Header/>
<section className='flex justify-items-start w-full'>
<button className='border-2 ml-7 mt-10 mb-16 rounded shadow-md hover:shadow-inner cursor-pointer w-3/12 flex flex-row items-center justify-center text-sm font-light'><span className='mr-2 text-lg'><BsArrowLeft/></span>Back</button>
</section>
{details && details.map((data,index) =>
<div className='w-full flex flex-col items-center' key={index}>
<img className='rounded mb-11 w-10/12 h-6/6' alt={data.name.common} src={data.flags.png}/>
<section className='flex flex-col w-full '>
<h1 className='font-extrabold text-xl ml-7'>{data.name.common}</h1>
<p className='ml-7 mt-4 text-sm font-light'><span className='text-sm font-semibold'>Official Name: </span>{data.name.official}</p>
<p className='ml-7 mt-2 text-sm font-light'><span className='text-sm font-semibold'>Population: </span>{data.population}</p>
<p className='ml-7 mt-2 text-sm font-light'><span className='text-sm font-semibold'>Region: </span>{data.region}</p>
<p className='ml-7 mt-2 text-sm font-light'><span className='text-sm font-semibold'>Sub Region: </span>{data.subregion}</p>
<p className='ml-7 mt-2 text-sm font-light'><span className='text-sm font-semibold'>Capital: </span>{data.capital}</p>
<p className='ml-7 mt-8 text-sm font-light'><span className='text-sm font-semibold'>Top Level Domain: </span>{data.tld}</p>
<p className='ml-7 mt-2 text-sm font-light'><span className='text-sm font-semibold'>Currencies: </span>{data.currencies[string].name}</p>
<p className='ml-7 mt-2 text-sm font-light'><span className='text-sm font-semibold'>Languages: </span></p>
<h2 className='font-semibold text-base ml-7 mt-8'>Border Countries:</h2>
<button className='border-2 shadow-md rounded cursor-pointer w-24 h-6 ml-7 mt-4 text-xs font-light'>{data.borders}</button>
</section>
</div>)}
</div>
</main>
);
}
export default Country;
Upvotes: 0
Views: 55
Reputation: 1270
The problem seems to be with the currency key string accessing code in useEffect(). Based on the data returned from the API, the currencies information can be accessed with below code. This works good when only single currency is returned for a country.
// setString(Object.keys(data.currencies).toString());
setString(Object.keys(data[0] && data[0].currencies || {}).toString());
Upvotes: 1
Reputation: 41
Your two set states run independently so you have no guarantee about which finished first, adding in a check for both will make sure you have no nulls.
import React, {useState,useEffect} from 'react'
import {useParams} from 'react-router-dom'
import Header from '../components/header.js'
import{BsArrowLeft} from 'react-icons/bs'
const Country = () => {
const {countryName} = useParams();
const [country,setCountry] = useState(countryName);
const [details,setDetails] = useState(null);
const [string,setString] = useState(null);
useEffect( () => {
async function fetchData(){
const res = await fetch(`https://restcountries.com/v3.1/name/${country}`)
const data = await res.json()
setDetails(data)
setString(Object.keys(data.currencies).toString())
}
fetchData()
}, [country])
return (
<main className='h-screen flex flex-col items-center w-full overflow-x-hidden bg-verylightgrey dark:bg-verydarkblue overflow-scroll'>
<div className='flex flex-col items-center w-full'>
<Header/>
<section className='flex justify-items-start w-full'>
<button className='border-2 ml-7 mt-10 mb-16 rounded shadow-md hover:shadow-inner cursor-pointer w-3/12 flex flex-row items-center justify-center text-sm font-light'><span className='mr-2 text-lg'><BsArrowLeft/></span>Back</button>
</section>
{details && string && details.map((data,index) =>
<div className='w-full flex flex-col items-center' key={index}>
<img className='rounded mb-11 w-10/12 h-6/6' alt={data.name.common} src={data.flags.png}/>
<section className='flex flex-col w-full '>
<h1 className='font-extrabold text-xl ml-7'>{data.name.common}</h1>
<p className='ml-7 mt-4 text-sm font-light'><span className='text-sm font-semibold'>Official Name: </span>{data.name.official}</p>
<p className='ml-7 mt-2 text-sm font-light'><span className='text-sm font-semibold'>Population: </span>{data.population}</p>
<p className='ml-7 mt-2 text-sm font-light'><span className='text-sm font-semibold'>Region: </span>{data.region}</p>
<p className='ml-7 mt-2 text-sm font-light'><span className='text-sm font-semibold'>Sub Region: </span>{data.subregion}</p>
<p className='ml-7 mt-2 text-sm font-light'><span className='text-sm font-semibold'>Capital: </span>{data.capital}</p>
<p className='ml-7 mt-8 text-sm font-light'><span className='text-sm font-semibold'>Top Level Domain: </span>{data.tld}</p>
<p className='ml-7 mt-2 text-sm font-light'><span className='text-sm font-semibold'>Currencies: </span>{data.currencies[string].name}</p>
<p className='ml-7 mt-2 text-sm font-light'><span className='text-sm font-semibold'>Languages: </span></p>
<h2 className='font-semibold text-base ml-7 mt-8'>Border Countries:</h2>
<button className='border-2 shadow-md rounded cursor-pointer w-24 h-6 ml-7 mt-4 text-xs font-light'>{data.borders}</button>
</section>
</div>)}
</div>
</main>
);
}
Upvotes: 4