Ravenous part 4 Error 'TypeError: Cannot read property 'map' of undefined'

Hi Everyone, i’ve been comparing my code to the solutions for part 4 and trawling topics on this forum to identify possible solutions but can’t find any- any help would be appreciated.

I’ve identified there is a problem with my fetch request- it’s not returning the raw data or the JSON stuff. I tripled checked my api key which is correct. no where in the instructions does it advise to use your ‘clientid’ so i havent. Can anyone advise what i’m missing? I feel i’ve checked every obvious error.

I’m getting the following message on my screen:

TypeError: Cannot read property 'map' of undefined
BusinessList.render

I know why i’m receiving it- my fetch isnt getting the information so the mapping is trying to iterate through an empty object of undefined. -.-

my code:

Yelp.js:

const Yelp = {
  search(term, location, sortBy) {
    return fetch(`https://cors-anywhere.herokuapp.com/https://api.yelp.com/v3/businesses/search?term=${term}&location=${location}&sort_by=${sortBy}`, {
      headers: {
        Authorization: `Bearer ${apiKey}`
      }
    }).then(response => {
      console.log(response)
      return response.json();
    }).then(jsonResponse => {
      console.log(jsonResponse)
      if (jsonResponse.businesses) {
        return jsonResponse.businesses.map(business => ({
          id: business.id,
          imageSrc: business.image_url,
          name: business.name,
          address: business.location.address1,
          city: business.location.city,
          state: business.location.state,
          zipCode: business.location.zip_code,
          category: business.categories[0].title,
          rating: business.rating,
          reviewCount: business.review_count
        }));
      }
    });
  }
};

export default Yelp;

BusinessList.js:

import React from 'react';
import './BusinessList.css';

import Business from '../Business/Business';

class BusinessList extends React.Component {
  render() {
    return (
      <div className="BusinessList">
        {
           this.props.businesses.map(business => {
            return <Business business={business} key={business.id} />
          })
        }
      </div>
    );
  }
}

export default BusinessList;

Business.js:

import React from 'react';
import './Business.css';
class Business extends React.Component {
  render() {
    return (
      <div className="Business">
      <div className="image-container">
        <img src={this.props.business.imageSrc} alt=''/>
      </div>
      <h2>{this.props.business.name}</h2>
      <div className="Business-information">
        <div className="Business-address">
          <p>{this.props.business.address}</p>
          <p>{this.props.business.state}</p>
          <p>{this.props.business.city}</p>
          <p>{this.props.business.zipCode}</p>
        </div>
        <div className="Business-reviews">
          <h3>{this.props.business.category.toUpperCase()}</h3>
          <h3 className="rating">{`${this.props.business.rating} stars`}</h3>
          <p>{`${this.props.business.reviewCount} reviews`}</p>
        </div>
      </div>
    </div>
    )
  }

}

export default Business


Searchbar.js:

import React from 'react';
import './SearchBar.css';
class SearchBar extends React.Component {
  constructor(props) {
    super(props)
    this.state = {
     term: '',
     location: '',
     sortBy: 'best_match'
    }

    this.handleTermChange = this.handleTermChange.bind(this);
    this.handleLocationChange = this.handleLocationChange.bind(this);
    this.handleSearch = this.handleSearch.bind(this);
    this.handleSortByChange = this.handleSortByChange.bind(this);

    this.sortByOptions = {
      'Best Match': 'best_match',
      'Highest Rated': 'rating',
      'Most Reviewed': 'review_count'
    }
  }

  //getSortByClass() returns the current CSS class for a sorting option. 
  getSortByClass(sortByOption) {
  return this.state.sortBy === sortByOption ? 'active': '';
  }

  handleSortByChange(sortByOption) {
    this.setState({ sortBy: sortByOption});   
  }

  handleTermChange(e) {
    this.setState({term: e.target.value})
  }

  handleLocationChange(e) {
    this.setState({location: e.target.value})
  }

  handleSearch(e) {
    this.props.searchYelp(this.state.term, this.state.location, this.state.sortBy)
    e.preventDefault()
  }


  renderSortByOptions() {
    return Object.keys(this.sortByOptions).map(sortByOption => {
      let sortByOptionValue = this.sortByOptions[sortByOption];
    return <li 
    key={sortByOptionValue} 
    className={this.getSortByClass(sortByOptionValue)}
    onClick={this.handleSortByChange.bind(this, sortByOptionValue)}
    >{sortByOption}</li>
    });
    }

  render() {
    return (
     <div className="SearchBar">
     <div className="SearchBar-sort-options">
      <ul>
        {this.renderSortByOptions()}
      </ul>
    </div>
    <div className="SearchBar-fields">
      <input placeholder="Search Businesses" onChange={this.handleTermChange} />
      <input placeholder="Where?" onChange={this.handleLocationChange} />
    </div>
    <div className="SearchBar-submit">
      <a onClick={this.handleSearch}>Let's Go</a>
    </div>
  </div>
    )
  }
}

export default SearchBar;

Finally App.js:

import React from 'react';
import './App.css';

import BusinessList from '../BusinessList/BusinessList';
import SearchBar from '../SearchBar/SearchBar';
import Yelp from '../../util/Yelp';

class App extends React.Component {
  constructor(props) {
    super(props) 
      this.state = {
        businesses: []
      }
      this.searchYelp = this.searchYelp.bind(this) 
  }

  searchYelp(term, location, sortBy) {
    Yelp.search(term, location, sortBy).then(businesses => { 
      this.setState({businesses: businesses})
    })
  }

  render() {
  return (
    <div className="App">
    <h1>ravenous</h1>
    <SearchBar searchYelp={this.searchYelp}/>
    <BusinessList businesses={this.state.businesses} /> 
  </div>
    )
  }
}

export default App;

I’m seeing a lot of people get this error but i havent found a solution on my end. Any help would be appreciated. I feel i’m missing something very obvious.

Thanks in advance!

Hello,

Since you mentioned that you checked your api key to confirm it’s correct, I’m assuming you usually have a const apiKey = 'long_api_key_here'; at the top of your Yelp.js file, but you didn’t post it to keep it secret.

If that is accurate, then you actually aren’t missing anything that was included in the steps for the project. I dropped all of your files into my project (and added my apiKey) and was able to get back results:

I’ve found that Yelp’s APIs can be picky with location names, so I often use something I’m 100% sure will work for testing. For the screenshot above, I used the term: Pizza and the location: New York

Another thing that I’ve found is that the project steps don’t account for something going wrong, like Yelp not liking a search request. You could account for this in a number of ways, such as checking the value of businesses before calling this.setState with it. It’s not included in the steps though, so you didn’t miss it.

Congratulations on finishing the Ravenous project!

2 Likes

Hi Selectall,

Thanks so much for going out of your way to check my code- it was driving me nuts not being able to discern if there was indeed a bug. The problem is definitely with my key then!

I’ll have to see if i can somehow generate another key or try it your way and input pizza from new yor to see if it at least generates a valid response form the api.

You’re right, i think if the response is empty, then i’d want to render a placeholder of some sort- it doesnt explain the error message i get in my console which is what through me.

Thanks, i’ve moved onto Jamming for now but will come back to this project and iron it out before deploying hehe

1 Like

Hi Selectall,

I am having the same issue as sc6636956187 (our code is the same) but the difference is when I log the jsonResponse from Yelp.js to the console I see all the business objects but in App.js the business object from the .then() logs in the console logs as undefined, is this also an API key issue?

Hello @jbarlati, welcome to the forums.

Since you’re seeing all the business objects when you log the jsonResponse in Yelp.js, it doesn’t sound like an API key issue. It sounds like there could be a problem in what you’re doing with the jsonResponse after that, perhaps in your map() call or with how it’s being returned. If you show me your code, I can help with a second set of eyes on it.

Thanks @selectall,

I was thinking the same thing but wasn’t sure. Here is my code

Yelp.js

const Yelp = {

  search(term, location, sortBy) {

    return fetch(

      `https://cors-anywhere.herokuapp.com/https://api.yelp.com/v3/businesses/search?term=${term}&location=${location}&sort_by=${sortBy}`,

      { headers: { Authorization: `Bearer ${apiKey}` } }

    )

      .then((response) => {

        return response.json();

      })

      .then((jsonResponse) => {

        if (jsonResponse.businesses) {

          jsonResponse.businesses.map((business) => {

            console.log(business.name);

            return {

              id: business.id,

              imageSrc: business.image_url,

              name: business.name,

              address: business.location.address1,

              city: business.location.city,

              state: business.location.state,

              zipCode: business.location.zip_code,

              category: business.categories[0].title,

              rating: business.rating,

              reviewCount: business.review_count,

            };

          });

        }

      });

  },

};

export default Yelp;

App.js

import React from 'react';

import './App.css';

import BusinessList from '../BusinessList/BusinessList';

import SearchBar from '../SearchBar/SearchBar';

import Yelp from '../../util/yelp';

class App extends React.Component {

  constructor(props) {

    super(props);

    this.state = { businesses: [] };

    this.searchYelp = this.searchYelp.bind(this);

  }

  searchYelp(term, location, sortBy) {

    Yelp.search(term, location, sortBy).then((businesses) => {

      this.setState({ businesses: businesses });

    });

  }

  render() {

    return (

      <div className="App">

        <h1>ravenous</h1>

        <SearchBar searchYelp={this.searchYelp} />

        <BusinessList businesses={this.state.businesses} />

      </div>

    );

  }

}

export default App;

BusinessList.js

import React from 'react';

import './BusinessList.css';

import Business from '../Business/Business';

class BusinessList extends React.Component {

  render() {

    return (

      <div className="BusinessList">

        {this.props.businesses.map((business) => {

          console.log(business);

          return <Business business={business} />;

        })}

      </div>

    );

  }

}

export default BusinessList;

@jbarlati

You’re returning the object in your callback for the map(), but you’re not actually returning what map() returns so App.js only gets undefined.

return jsonResponse.businesses.map((business) => {

and you should be good to go

Oh wow, totally missed that somehow. Thanks @selectall !

1 Like

Hi @selectall , Im having the same problems as these two people here. I have checked my code time and time again, and I cannot find my mistake. I’ve already been stucked for 2 days now, and I would really like to finish this course in a good way. I’m already logging into the console the result from “reponse”

Response {type: "cors", url: "https://cors-anywhere.herokuapp.com/https://api.ye…/search?term=mexican&location=&sort_by=best_match", redirected: false, status: 400, ok: false, …}body: (...)bodyUsed: trueheaders: Headers {}ok: falseredirected: falsestatus: 400statusText: "Bad Request"type: "cors"url: "https://cors-anywhere.herokuapp.com/https://api.yelp.com/v3/businesses/search?term=mexican&location=&sort_by=best_match"__proto__: Response

Here is my Yelp.js file (apiKey omitted):

const Yelp = {
  searchYelp(term, location, sortBy) {
    return fetch(`https://cors-anywhere.herokuapp.com/https://api.yelp.com/v3/businesses/search?term=${term}&location=${location}&sort_by=${sortBy}`, {
      headers: {
        Authorization: `Bearer ${apiKey}`
      }
    }).then(response => {
        console.log(response);
      return response.json();
    }).then(jsonResponse => {
      if (jsonResponse.businesses) {
        return jsonResponse.businesses.map(business => ({
          id: business.id,
          imageSrc: business.image_url,
          name: business.name,
          address: business.location.address1,
          city: business.location.city,
          state: business.location.state,
          zipCode: business.location.zip_code,
          category: business.categories[0].title,
          rating: business.rating,
          reviewCount: business.review_count
        }));
      }
    });
  }
};

export default Yelp;

Here is my businessList.js file:

import React from 'react';
import './BusinessList.css';
import Business from './Business';


class BusinessList extends React.Component {
    render(){
        return (
            <div className="BusinessList">
            {
                this.props.businesses.map(business => {
                    return <Business key={business.id} business={business}/>;    
                }
                )
            }
            </div>
        )
    }
}

export default BusinessList;

and here is my App.js file (the other files were not touched, and until part 3 of the ravenous project I was doing ok)


import React from 'react';
import './App.css';
import BusinessList from './BusinessList'
import SearchBar from './SearchBar'
import Yelp from './util/Yelp'



class App extends React.Component{
  constructor(props) {
    super(props);
    this.state = {businesses: []};
    this.searchYelp = this.searchYelp.bind(this);
  }

  searchYelp(term, location, sortBy) {
    Yelp.searchYelp(term, location, sortBy).then((businesses) => {
      this.setState({businesses: businesses});
    });
  }
  render() {
  return (
    <div className="App">
      <h1>RAVENOUS</h1>
      <SearchBar searchYelp={this.searchYelp} />
      <BusinessList businesses={this.state.businesses}/> 
    </div>
  )
}
}

export default App;

Could you please help me out on this one?

Hi @javierortizmir599394,

Your code is also working fine (at least the ones you posted). I plugged your files into my project (and changed some of the paths just because my folder structure is different), and it works:

What might be happening is that you’re searching for something Yelp API doesn’t know how to handle. The error including “cors” may be throwing you off by making you think it’s cors related.

Looking at the error you received, it says it was a 400 error (Bad Request). The URL you tried to retrieve was:

https://api.yelp.com/v3/businesses/search?term=mexican&location=&sort_by=best_match

If you only searched for “mexican” without a location, then that’s why you got the error. If you did provide a location in your search, then you need to double check how SearchBar.js is calling your App’s searchYelp function. Maybe it isn’t passing along the location. You didn’t post that code so I can’t be sure

Hi @selectall … thank you very much for your answer!!

This is my searchBar code:


 import React from 'react';
import './SearchBar.css';

const sortByOptions = {
    'Best Match': 'best_match',
    'Highest Rated': 'rating',
    'MostReviewed': 'review_count'
};

class SearchBar extends React.Component {
    constructor(props) {
      super(props);
      this.state = {term: '',
                    location: '',
                    sortBy: 'best_match'};
      this.sortByOptions = {
          'Best Match': 'best_match',
          'Highest Rated': 'rating',
          'Most Reviewed': 'review_count'
           };
      this.handleTermChange = this.handleTermChange.bind(this);
      this.handleLocationChange = this.handleLocationChange.bind(this);
      this.handleSearch = this.handleSearch.bind(this);
    }
    getSortByClass(sortByOption) {
     if(this.state.sortBy === sortByOption) {
       return 'active';
     } else {return '';}
    }

    handleSortByChange(sortByOption) {
      this.setState({sortBy: sortByOption});
    }

    handleTermChange(event) {
      this.setState({term: event.target.value});
    }

    handleLocationChange(event) {
      this.setState({location: event.target.value});
    }

    handleSearch(event) {
      this.props.searchYelp(this.state.term, this.state.location, this.state.sortBy);
      event.preventDefault();
    }

    renderSortByOptions() {
        return Object.keys(this.sortByOptions).map(sortByOption => {
            let sortByOptionValue = this.sortByOptions[sortByOption];
            return <li className={this.getSortByClass(sortByOptionValue)} onClick={this.handleSortByChange.bind(this, sortByOptionValue)} key={sortByOptionValue}> {sortByOption} </li>;
        });
    }

    render() {
        return(
            <div className="SearchBar">
            <div className="SearchBar-sort-options">
              <ul>
                {this.renderSortByOptions()}
              </ul>
            </div>
            <div className="SearchBar-fields">
              <input onChange={this.handleTermChange} placeholder="Search Businesses" />
              <input onchange={this.handleLocationChange} placeholder="Where?" />
            </div>
            <div className="SearchBar-submit">
              <a onClick={this.handleSearch}>Let's Go</a>
            </div>
          </div>   
        )
    }
}

export default SearchBar;

Hey @javierortizmir599394

You have a typo for your input event for location, which would explain why your location was blank in the previous request. handleLocationChange() never gets called so the state never gets updated