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;