I am learning ReactJS and building a small app that fetches data from an API and renders it on the page. I have a sidebar that has a list of regions and makes an API call to retrieve all countries in the region on click.

    function getOccurrence(array, value) {
      return array.filter((v) => (v === value)).length;
    }

    let uniqueRegions = totalRegions.filter((v, i, a) => a.indexOf(v) === i);

    let lis = [];
    for (let i = 0; i < uniqueRegions.length -1; i++) {
      lis.push(
        <li className="nav-item" key={i} onClick={props.sideBar}>
          <span className="nav-link bg-success mb-1"><strong>{uniqueRegions[i]}</strong> - {getOccurrence(totalRegions, uniqueRegions[i])}</span>
          <ul>
          {this.props.regionData.map((country, index) => <li key={index}>{country.name}</li>) || null}
          </ul>
        </li>

      )

    }

However, the initial click calls the function and returns an undefined array. When mapping over the briefly undefined array, it crashes the app. I'd like to map over the array and return an li for each item in the array.

I've attempted conditional rendering the desired element {country.name} and returning null if the array is undefined, but when the array is defined, the desired element doesn't render. Below is the function that runs on click, and the callback function that sets state on the regional data.

  sideBarData = () => {
    let totalRegions = this.state.nations.map(a => a.region)
    function getOccurrence(array, value) {
      return array.filter((v) => (v === value)).length;
    }
    let uniqueRegions = totalRegions.filter((v, i, a) => a.indexOf(v) === i);
    for (let i = 0; i < uniqueRegions.length -1; i++) {
      this.setState({
        regionData: this.findAllByRegion(uniqueRegions[i])
      })
    }
  }

  findAllByRegion = (region) =>{
    return Axios
     .get('https://restcountries.eu/rest/v2/region/' + region)
     .then(res => {
       if(res.status === 200 && res !== null){
        this.setState(prevState =>({
          regionData: res.data
        }))
         console.log(this.state.regionData);
       } else {
         throw new Error('No country found');
       }
       })
       .catch(error => {
         console.log(error)
         return []
       });
   };

I expect the line {this.props.regionData.map((country, index) => <li key={index}>{country.name}</li>) || null} to be null when the array is undefined, but the DOM doesn't update the li when the array is defined.

2 Answers

0
julekgwa On Best Solutions

Just check if you get valid data from the api, where you set your state do the following, in your findAllByRegion

this.setState(prevState =>({
  // always validate your response.
  // if res is undefined doing res.data will crash your app
   regionData: res && res.data || []  
}))
0
Sergio Moura On

you can't use undefined.map. You need to make sure that the value is an array, before mapping trough it.

For example:

   function getOccurrence(array, value) {
      return array.filter((v) => (v === value)).length;
    }

    let uniqueRegions = totalRegions.filter((v, i, a) => a.indexOf(v) === i);

    let lis = [];

    // this next line here makes sure you always have an array
    const regionData = this.props.regionData || [];

    for (let i = 0; i < uniqueRegions.length -1; i++) {
      lis.push(
        <li className="nav-item" key={i} onClick={props.sideBar}>
          <span className="nav-link bg-success mb-1"><strong>{uniqueRegions[i]}</strong> - {getOccurrence(totalRegions, uniqueRegions[i])}</span>
          <ul>
          {/* never undefined */regionData.map((country, index) => <li key={index}>{country.name}</li>) || null}
          </ul>
        </li>
      )
    }