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;
CodePudding user response:
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>
);
}
CodePudding user response:
try:
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 () => {
async function fetchData(){
const res = await fetch(`https://restcountries.com/v3.1/name/${country}`)
return await res.json()
}
const data = await fetchData()
setDetails(data)
setString(Object.keys(data.currencies).toString())
}, [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>