Reputation: 2572
I am building a small React Routing application to get a better idea as to how it work. My App.js looks like this with the basic routing:
import React from 'react';
import './App.css';
import Nav from './Nav';
import About from './About';
import Shop from './Shop';
import CountryDetail from './CountryDetail'
import { BrowserRouter as Router, Switch, Route } from 'react-router-dom';
function App() {
return (
<Router>
<div className="App">
<Nav />
<Switch>
<Route path="/" exact component={Home} />
<Route path="/about" component={About} />
<Route path="/shop" exact component={Shop} />
<Route path="/shop/:name" component={CountryDetail} />
</Switch>
</div>
</Router>
);
}
const Home = () => (
<div>
<h1>Home Page</h1>
</div>
);
Now the Shop component a list of countries from the api which is in the code below:
import React from 'react';
import './App.css';
import { useEffect } from 'react';
import { useState } from 'react';
import {Link} from 'react-router-dom';
function Shop() {
useEffect(() => {
fetchItems();
},[])
const [countries, setCountries] = useState([])
const fetchItems = async () => {
const data = await fetch('https://restcountries.eu/rest/v2/all');
const countries = await data.json();
console.log(countries);
setCountries(countries);
}
return (
<div>
{countries.map(country => (
<div>
<Link to={`shop/${country.name}`}>
<h1 key={country.alpha2Code}>
{country.name}
</h1>
</Link>
<p>Popluation {country.population}</p>
<p> Region {country.region}</p>
<p>Capital {country.capital}</p>
</div>
)
)}
</div>
);
}
export default Shop;
Now what I want to do is render more information about the country when I click on it. So I have created another component called CountryDetail:
import React from 'react';
import './App.css';
import { useEffect } from 'react';
import { useState } from 'react';
function CountryDetail( { match } ) {
useEffect(() => {
fetchItem();
console.log(match)
},[])
const [country, setCountry] = useState([])
const fetchItem = async ()=> {
const fetchCountry = await fetch(`https://restcountries.eu/rest/v2/name/${match.params.name}`);
const country = await fetchCountry.json();
setCountry(country);
console.log(country);
}
return (
<div>
<h1>Name {country.name}</h1>
<p>Native Name{country.nativeName}</p>
<p>Region {country.region}</p>
<p>Languages {country.languages}</p>
<h1>This Country</h1>
</div>
);
}
export default CountryDetail;
The problem I am having is that it is not rendering anything on the CountryDetail page. I am sure I have hit the api correctly but not sure if I am getting the data correctly. Any help would be appreciated.
Upvotes: 0
Views: 53
Reputation: 203208
Issue: The returned JSON is an array but your JSX assumes it is an object.
Solution: You should extract the 0th element from the JSON array. Surround in a try/catch in case of error, and correctly render the response.
Note: the languages is also an array, so that needs to be mapped
function CountryDetail({ match }) {
useEffect(() => {
fetchItem();
console.log(match);
}, []);
const [country, setCountry] = useState(null);
const fetchItem = async () => {
try {
const fetchCountry = await fetch(
`https://restcountries.eu/rest/v2/name/${match.params.name}`
);
const country = await fetchCountry.json();
setCountry(country[0]);
console.log(country[0]);
} catch {
// leave state alone or set some error state, etc...
}
};
return (
country && (
<div>
<h1>Name {country.name}</h1>
<p>Native Name{country.nativeName}</p>
<p>Region {country.region}</p>
<p>Languages {country.languages.map(({ name }) => name).join(", ")}</p>
<h1>This Country</h1>
</div>
)
);
}
Upvotes: 1
Reputation: 4783
As you said it yourself, the response is an array (with a single country object in it), but you are using it as if it would be an object.
So, instead of:
const country = await fetchCountry.json();
setCountry(country);
It should be:
const countries = await fetchCountry.json();
setCountry(countries[0]);
Upvotes: 0