AltBrian
AltBrian

Reputation: 2572

Not able to access detail information from Api using React Router not rendering on the page

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

Answers (2)

Drew Reese
Drew Reese

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>
    )
  );
}

Edit affectionate-merkle-74weu

Upvotes: 1

zord
zord

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

Related Questions