Greg
Greg

Reputation: 256

Fetch request in React: How do I Map through JSON array of object inside of array of objects?

I managed to fetch API and could output some data in browser, but I couldn't handle an array of object in JSON. It's a rest country API, where some countries have more than 1 language. I want to output all languages they speak. Here is the API link. And here is my code

import React, { useState, useEffect } from "react";
import CountryListCard from "./CountryListCard";

import "./CountryList.scss";

export default function CountryList() {
  const [data, setData] = useState([]);

  const fetchData = () => {
    fetch("https://restcountries.eu/rest/v2/all")
      .then((res) => res.json())
      .then((result) => setData(result))
      .catch((err) => console.log("error"));
  };

  useEffect(() => {
    fetchData();
  }, []);

  return (
    <div>
      {data &&
        data.map((element, index) => (
          <CountryListCard
            image={element.flag}
            name={element.name}
            key={index}
            region={element.region}
            population={element.population}
           {/* language={element.languages[0]}   this doesn't work*/}
          />
        ))}
      {/* {data.languages &&
        data.languages.map((element, index) => (  
          <CountryListCard key={index} language={element.languages.iso639_1} />  this doesn't work
        ))} */}
    </div>
  );
}


Upvotes: 2

Views: 1260

Answers (3)

Iago Calazans
Iago Calazans

Reputation: 328

@Greg! This is how it works with Axios and if you will not reuse CountryListCard it could be in the same file as CountryList.

import React, { useState, useEffect } from "react";
import Axios from "axios";

export default function CountryList() {
  const [data, setData] = useState([]);

  const fetchData = async () => {
    try {
      const countries = await Axios.get("https://restcountries.eu/rest/v2/all");
      setData(countries.data);
    } catch (err) {
      console.log(err);
    }
  };

  useEffect(() => {
    fetchData();
  }, []);

  return (
    <div>
      {data?.map((country, i) => (
        <CountryListCard
          name={country.name}
          flag={country.flag}
          key={i}
          region={country.region}
          population={country.population}
          languages={country.languages}
          alphaCode={country.alpha3code}
        />
      ))}
    </div>
  );
}

function CountryListCard(props) {
  const [country /*In this case we don't need setCountry*/] = useState(props);

  return (
    <div id={country.alphaCode} className="country">
      <h3>
        <img src={country.flag} width="18" alt={country.alphaCode} /> {country.name}
      </h3>
      <b>Region</b>: {country.region} <br />
      <b>Population</b>: {country.population} <br />
      <b>Languages</b>:{" "}
      {country.languages?.map((lang, i) => (
        <li key={i}>{lang.name}</li>
      ))}
    </div>
  );
}

The code above will look like this: View result

Upvotes: 0

Chcamiloam
Chcamiloam

Reputation: 594

you should call the languages map inside your country map like:

countries.map(country=>(
<div key={country.name}>
    <h1>{country.name}</h1>
    {country.languages.map((language, languageIndex)=>(
        <p key={languageIndex}>{language.name}</p>
     ))}
</div>
))

Also, it is not related with the post, but I'll suggest you to not use generic names in your .map like item/element/obj

Upvotes: 1

Dominik
Dominik

Reputation: 6313

This should work:

{
  data.languages &&
    data.languages.map((element) =>
      element.languages.map((language, index) => (
        <CountryListCard key={index} language={language.iso639_1} />
      ))
    );
}

Upvotes: 0

Related Questions