1

I am creating a find nearest restaurant app using Google Places API. I am calling for a background image in my ResultItem component in a prop <ResultsItem name={places.name} image={Thumbnail} rating={places.rating} rating_total={places.user_ratings_total} />

This image is defined as Thumbnail in const above this part of the code. My code runs smoothly but as soon as places.photos[0] returns as undefined (meaning that it is Google Place that doesn't have any image uploaded) i get an error saying:

Unhandled Rejection (TypeError): Cannot read property '0' of undefined

I think what I have to do is check whether places.photos[0] is undefined or not but I do not seem to get it right...

My goal is to display another placeholder image when this value turns out undefined. If it is defined though the component should take the image from google places api.

Can someone help me?

FULL COMPONENT:

import React, { Component } from 'react';

// Imports
import axios from 'axios';
import Script from 'react-load-script';
import Placeholder from './Placeholder.jsx';
import FadeIn from 'react-fade-in';
import {
  Spinner,
  Paragraph,
  SideSheet,
  Tooltip,
  IconButton,
  SearchInput
} from 'evergreen-ui';
import ResultsItem from '../../components/ResultsItem/ResultsItem.jsx';

import Geocode from 'react-geocode';

// Styles
import './Search.scss';

class Autocomplete extends Component {
  // Define Constructor
  constructor(props) {
    super(props);

    // Declare State
    this.state = {
      type: 'restaurant',
      radius: 10,
      lat: '59.0738',
      lng: '41.3226',
      city: '',
      query: '',
      open: false,
      places: [],
      place_detail: [],
      sidebar: false,
      loading: true
    };

    this.currentLocationOnClick = this.currentLocationOnClick.bind(this);
    this.handlePlaceSelect = this.handlePlaceSelect.bind(this);
  }

  currentLocationOnClick = async () => {
    let { lat, lng, places } = this.state;
    const URL = `https://maps.googleapis.com/maps/api/place/nearbysearch/json?location=${lat},${lng}&type=restaurant&radius=${5 *
      1000}&key=MY_API_KEY`;
    navigator.geolocation.getCurrentPosition(
      async position => {
        this.setState({ lat: position.coords.latitude });
        this.setState({ lng: position.coords.longitude });

        const URL = `https://maps.googleapis.com/maps/api/place/nearbysearch/json?location=${
          position.coords.latitude
        },${position.coords.longitude}&type=restaurant&radius=${5 *
          1000}&key=MY_API_KEY`;

        const response = await axios.get(URL);
        console.log(response.data);
        places = response.data.results;
        this.setState({ places });
      },
      error => {
        console.log('Error getting location');
      }
    );
  };

  async componentDidMount() {
    const url = `https://maps.googleapis.com/maps/api/place/nearbysearch/json?location=
    ${this.state.lat},${this.state.lng}type=restaurant&radius=${2 *
      1000}&key=MY_API_KEYE`;
    const response = await fetch(url);
    const data = await response.json();
    this.setState({ places: data.results });
    console.log(data.results);
  }

  handleScriptLoad = () => {
    // Declare Options For Autocomplete
    const options = {
      types: ['address']
    }; // To disable any eslint 'google not defined' errors

    // Initialize Google Autocomplete
    /*global google*/ this.autocomplete = new google.maps.places.Autocomplete(
      document.getElementById('autocomplete'),
      options
    );

    // Avoid paying for data that you don't need by restricting the set of
    // place fields that are returned to just the address components and formatted
    // address.
    this.autocomplete.setFields(['address_components', 'formatted_address']);

    // Fire Event when a suggested name is selected
    this.autocomplete.addListener('place_changed', this.handlePlaceSelect);
  };

  handlePlaceSelect = async () => {
    let { query, lat, lng } = this.state;

    this.setState({ loading: true });

    // Extract City From Address Object
    const addressObject = this.autocomplete.getPlace();
    const address = addressObject.address_components;

    Geocode.setApiKey('MY_API_KEY');

    // Check if address is valid
    let city;
    if (address) {
      city = address[0].long_name;
      query = addressObject.formatted_address;
    }

    try {
      const response = await Geocode.fromAddress(query);
      ({ lat, lng } = response.results[0].geometry.location);
    } catch (error) {
      console.error(error);
    }

    let places;
    try {
      const URL = `https://maps.googleapis.com/maps/api/place/nearbysearch/json?location=${lat},${lng}&type=restaurant&radius=${5 *
        1000}&key=MY_API_KEY`;
      const response = await axios.get(URL);
      console.log(response.data);
      places = response.data.results;
    } catch (error) {
      console.log(error.message);
    }

    this.setState({ query, places, city, lat, lng });
    setTimeout(() => this.setState({ loading: false }), 400);
  };

  render() {
    const { loading } = this.state;

    return (
      <div>
        <div className="flex align-center">
          <div className="search">
            <SearchInput
              id="autocomplete"
              placeholder="Search by address"
              width="100%"
              height={56}
            />
            <Script
              url="https://maps.googleapis.com/maps/api/js?key=MY_API_KEY&libraries=places,geometry&callback=initAutocomplete"
              onLoad={this.handleScriptLoad}
            />
          </div>

          <div className="current-location">
            <Tooltip content="Use current location">
              <IconButton
                icon="locate"
                iconSize={16}
                height={32}
                onClick={this.currentLocationOnClick}
              >
                {this.state.lat} & {this.state.lng}
              </IconButton>
            </Tooltip>
          </div>
        </div>

        <div className="results">
          {this.state.places.map(places => {
            const Thumbnail = `https://maps.googleapis.com/maps/api/place/photo?maxwidth=400&photoreference=${places.photos[0].photo_reference}&key=MY_API_KEY
    `;
            return (
              <div
                className="results-flex"
                onClick={async () => {
                  let loading;
                  let sidebar;
                  let place_detail;
                  try {
                    const URL = `https://maps.googleapis.com/maps/api/place/details/json?place_id=${places.place_id}&fields=name,rating,formatted_phone_number&key=
                    MY_API_KEY`;
                    const response = await axios.get(URL);
                    console.log(response.data);
                    place_detail = response.data.result;
                  } catch (error) {
                    console.log(error.message);
                  }

                  this.setState(
                    { place_detail, sidebar: true }
                    // () => this.props.sideBarOpen()
                  );
                }}
              >
                {this.state.loading ? (
                  <>
                    <div className="flex justify-center">
                      <Placeholder />
                    </div>
                  </>
                ) : (
                  <FadeIn>
                    <ResultsItem
                      name={places.name}
                      image={Thumbnail}
                      rating={places.rating}
                      rating_total={places.user_ratings_total}
                    />
                  </FadeIn>
                )}
              </div>
            );
          })}

          <SideSheet
            isShown={this.state.sidebar}
            onCloseComplete={() => this.setState({ sidebar: false })}
          >
            <Paragraph margin={40}>{this.state.place_detail.name}</Paragraph>
          </SideSheet>
        </div>
      </div>
    );
  }
}

export default Autocomplete;

2 Answers 2

2

I think the best solution is to assign it to a variable :

 const photoReference = places && places.photos[0] ? places.photos[0].photo_reference : placeholder;
Sign up to request clarification or add additional context in comments.

2 Comments

Hi. I cannot seem to get it right within my code.. Could you maybe try to show me how would you incorporate this solution in the code?
this should remove the error you got: stackblitz.com/edit/react-boilerplate-scalabw-utgktb
0

try using if else for it

for eg:

if(!!places.photos[0]) {   
const Thumbnail =
`https://maps.googleapis.com/maps/api/place/photo?maxwidth=400&photoreference=${places.photos[0].photo_reference}&key=MY_API_KEY
}
else { const Thumbnail = `another image url` }

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.