0

I am making a small React App that getting data from a Countries Api. I want to use the Select tag in React. This is so that I can filter the regions of the countries on the page. So if someone selects Europe it returns European Countries, or Africa then African countries and so on. I have looked at this post for using the select tag . However, the issue I have is that the list keeps repeating itself.enter image description here

Below is the code that I have so far:

import React, { Component } from 'react';
import { CountryList } from './Components/Card-List/CountryList';
import { SearchBox } from './Components/Search-box/Search-Box';
import { NavBarCard }from './Components/NavBar/NavBarCard';
import './Countries.styles.css';


class Countries extends Component {
constructor() {
    super();
    this.state = {
        countries:[],
        searchField:"",
        regionField:"",
        darkMode: false
    }
    this.setDarkMode = this.setDarkMode.bind(this);
    // this.handleRegion = this.handleRegion.bind(this);
   };


componentDidMount() {
    fetch("https://restcountries.eu/rest/v2/all")
    .then(response => response.json())
    .then(all =>  this.setState({ countries: all,
        regions: all}))
}
setDarkMode(e){
    this.setState((prevState) => ({ darkMode: !prevState.darkMode }));
}

handleRegion = (e) => {
    this.setState({regionField: e.target.value})
}
render() {
    const { countries, searchField, regionField, darkMode } = this.state;
    const filterCountries = countries.filter((country) => country.name.toLowerCase().includes(searchField.toLowerCase()) &&
     country.region.toLowerCase().includes(regionField.toLowerCase()));

     return(

            <div className={darkMode ? "dark-mode" : "light-mode" }>

                 <NavBarCard handlechange={this.setDarkMode} moonMode={darkMode ? "moon fas fa-moon" : "moon far fa-moon"} darkMode={darkMode ? "dark-mode" : "light-mode"}/>


                <div className="Input">

                    < SearchBox type="search" placeholder="Search a Country" handlechange={e=> this.setState({
                        searchField: e.target.value })}
                        />

                        {/* < SearchBox type="regions" placeholder="Filter by Regions" handlechange={e=> this.setState({
                            regionField: e.target.value })}
                            /> */}
                        <select onChange={this.handleRegion}>
                            {countries.map(region => (
                                <option key={region.alpha2Code}>
                                    {region.region}
                                </option>
                            ))}
                        </select>

                </div>
                <CountryList countries={filterCountries} />

            </div>

       )
     }
   }

  export default Countries

Not sure what I have missed. Any help would be appreciated.

AltBrian
  • 2,392
  • 9
  • 29
  • 58

2 Answers2

2

It´s clear that your problem is not in the code itself, it is the way you handle the data. You are mapping the countries array, so for each country there is a region field in it. What you are getting back in the select tag is all the countries but instead of showing their names they are showing the region they belong to.

  • Oh I see! Thanks for the heads up. – AltBrian Apr 27 '20 at 13:18
  • If you want to iterate over the countries array and retrieve the unique names for regions you can do something like this: ```new Set(countries.map(country=>country.region)).map(uniqueRegion=> ``` – Daniel Herrero Hernando Apr 27 '20 at 13:22
  • Using the Set type you can clean an array and get back only the unique values in it, which suits your case as you may have an array with 200 countries each one with a region like 'America', 'Europe'... repeated many times – Daniel Herrero Hernando Apr 27 '20 at 13:23
  • do you mean like this `````` – AltBrian Apr 27 '20 at 13:32
  • yes, exactly like this `````` – Daniel Herrero Hernando Apr 27 '20 at 13:37
  • I'm getting the following error ```TypeError: (intermediate value).map is not a function``` – AltBrian Apr 27 '20 at 13:40
  • 1
    Sorry, I forgot that before mapping the Set type you need to convert it to an array, so the code would be like this: `````` – Daniel Herrero Hernando Apr 27 '20 at 20:19
0

You're passing Countries to this <CountryList countries={filterCountries} />. I hope you're not looping to render the countries in this component.

This:

<select onChange={this.handleRegion}>
    {countries.map(region => (
        <option key={region.alpha2Code}>
            {region.region}
        </option>
    ))}
</select>

is correct but you need to add value attribute <select onChange {this.handleRegion} value={regionField}>

Also uncomment the binding with the this keyword in the constructor for handleRegion method.

Edited

let regions = [];
    fetch("https://restcountries.eu/rest/v2/all")
        .then(response => response.json())
        .then(all => {
            // Get unique regions here using Set
            let uniqueRegions = new Set();
            all.forEach(item => {
                uniqueRegions.add(item.region);
            });

            for (item of uniqueRegions) {
                regions.push(item);
            };

            this.setState({
                countries: all,
                regions: regions
            })
        });
Isah Ohieku
  • 1
  • 1
  • 2
  • This does not solve my problem. As Daniel says I am mapping over the countries and showing all the names of the region it belongs to (see image above). He has given me a solution but it is not working. – AltBrian Apr 27 '20 at 14:12
  • AltBrian, Just added a filter to get regions only. I hope it helps – Isah Ohieku Apr 27 '20 at 14:37
  • How would I add a filter to get the regions? Thanks for your help Isah Ohieku – AltBrian Apr 27 '20 at 15:17
  • Look at my post again and you would see the code block I added after **Edited**. I used **Set** to achieve it. – Isah Ohieku Apr 27 '20 at 18:37