React.js Google Maps API integration with components and hooks for seamless Google Maps functionality
—
Quality
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Components for integrating Google Places API functionality including autocomplete, place search capabilities, and location-based services with comprehensive place data access.
Provides place autocomplete functionality for input fields with extensive filtering and customization options.
/**
* Provides place autocomplete functionality for input fields
* Enhances text inputs with intelligent place suggestions and validation
*/
interface AutocompleteProps {
children: React.ReactNode; // Required - must contain exactly one input element
bounds?: google.maps.LatLngBounds | google.maps.LatLngBoundsLiteral;
restrictions?: google.maps.places.ComponentRestrictions;
fields?: string[];
options?: google.maps.places.AutocompleteOptions;
types?: string[];
// Event handlers
onPlaceChanged?: () => void;
// Lifecycle events
onLoad?: (autocomplete: google.maps.places.Autocomplete) => void;
onUnmount?: (autocomplete: google.maps.places.Autocomplete) => void;
}
interface google.maps.places.AutocompleteOptions {
bounds?: google.maps.LatLngBounds | google.maps.LatLngBoundsLiteral;
componentRestrictions?: google.maps.places.ComponentRestrictions;
fields?: string[];
placeIdOnly?: boolean;
strictBounds?: boolean;
types?: string[];
}
interface google.maps.places.ComponentRestrictions {
country?: string | string[];
}
function Autocomplete(props: AutocompleteProps): JSX.Element;Usage Examples:
import React, { useState, useRef } from 'react';
import { GoogleMap, LoadScript, Autocomplete } from '@react-google-maps/api';
// Basic autocomplete
function BasicAutocomplete() {
const [selectedPlace, setSelectedPlace] = useState<google.maps.places.PlaceResult | null>(null);
const autocompleteRef = useRef<google.maps.places.Autocomplete | null>(null);
const onLoad = (autocomplete: google.maps.places.Autocomplete) => {
autocompleteRef.current = autocomplete;
};
const onPlaceChanged = () => {
if (autocompleteRef.current) {
const place = autocompleteRef.current.getPlace();
setSelectedPlace(place);
console.log('Selected place:', place);
}
};
return (
<LoadScript googleMapsApiKey="YOUR_API_KEY" libraries={['places']}>
<div>
<div style={{ padding: '10px' }}>
<Autocomplete onLoad={onLoad} onPlaceChanged={onPlaceChanged}>
<input
type="text"
placeholder="Enter a place"
style={{
boxSizing: 'border-box',
border: '1px solid transparent',
width: '240px',
height: '32px',
padding: '0 12px',
borderRadius: '3px',
boxShadow: '0 2px 6px rgba(0, 0, 0, 0.3)',
fontSize: '14px',
outline: 'none'
}}
/>
</Autocomplete>
</div>
{selectedPlace && (
<div style={{ padding: '10px', background: '#f0f0f0' }}>
<h4>Selected Place:</h4>
<div>Name: {selectedPlace.name}</div>
<div>Address: {selectedPlace.formatted_address}</div>
{selectedPlace.geometry && (
<div>
Coordinates: {selectedPlace.geometry.location?.lat()}, {selectedPlace.geometry.location?.lng()}
</div>
)}
</div>
)}
<GoogleMap
center={
selectedPlace?.geometry?.location
? selectedPlace.geometry.location.toJSON()
: { lat: 40.7128, lng: -74.0060 }
}
zoom={15}
mapContainerStyle={{ width: '100%', height: '400px' }}
/>
</div>
</LoadScript>
);
}
// Advanced autocomplete with restrictions and fields
function AdvancedAutocomplete() {
const [selectedPlace, setSelectedPlace] = useState<google.maps.places.PlaceResult | null>(null);
const [country, setCountry] = useState<string>('us');
const [placeType, setPlaceType] = useState<string>('establishment');
const autocompleteRef = useRef<google.maps.places.Autocomplete | null>(null);
// Specify which place data fields to retrieve
const placeFields = [
'place_id',
'name',
'formatted_address',
'geometry',
'types',
'rating',
'price_level',
'opening_hours',
'photos',
'international_phone_number',
'website'
];
const placeTypes = [
{ value: 'establishment', label: 'Establishments' },
{ value: 'geocode', label: 'Geocodes' },
{ value: 'address', label: 'Addresses' },
{ value: '(cities)', label: 'Cities' },
{ value: '(regions)', label: 'Regions' }
];
const onLoad = (autocomplete: google.maps.places.Autocomplete) => {
autocompleteRef.current = autocomplete;
};
const onPlaceChanged = () => {
if (autocompleteRef.current) {
const place = autocompleteRef.current.getPlace();
setSelectedPlace(place);
console.log('Place details:', place);
}
};
return (
<LoadScript googleMapsApiKey="YOUR_API_KEY" libraries={['places']}>
<div>
<div style={{ padding: '10px', background: '#f0f0f0' }}>
<div style={{ marginBottom: '10px' }}>
<label style={{ marginRight: '10px' }}>
Country:
<select
value={country}
onChange={(e) => setCountry(e.target.value)}
style={{ marginLeft: '5px' }}
>
<option value="us">United States</option>
<option value="ca">Canada</option>
<option value="gb">United Kingdom</option>
<option value="au">Australia</option>
<option value="de">Germany</option>
<option value="fr">France</option>
</select>
</label>
<label>
Place Type:
<select
value={placeType}
onChange={(e) => setPlaceType(e.target.value)}
style={{ marginLeft: '5px' }}
>
{placeTypes.map(type => (
<option key={type.value} value={type.value}>
{type.label}
</option>
))}
</select>
</label>
</div>
<Autocomplete
onLoad={onLoad}
onPlaceChanged={onPlaceChanged}
restrictions={{ country }}
types={[placeType]}
fields={placeFields}
options={{
strictBounds: false,
componentRestrictions: { country }
}}
>
<input
type="text"
placeholder={`Search for ${placeTypes.find(t => t.value === placeType)?.label.toLowerCase()} in ${country.toUpperCase()}`}
style={{
width: '100%',
height: '40px',
padding: '0 15px',
fontSize: '16px',
border: '2px solid #4285f4',
borderRadius: '8px',
outline: 'none',
boxShadow: '0 2px 8px rgba(0, 0, 0, 0.1)'
}}
/>
</Autocomplete>
</div>
{selectedPlace && (
<div style={{ padding: '15px', background: 'white', margin: '10px', borderRadius: '8px', boxShadow: '0 2px 8px rgba(0, 0, 0, 0.1)' }}>
<h4>{selectedPlace.name}</h4>
<div><strong>Address:</strong> {selectedPlace.formatted_address}</div>
<div><strong>Types:</strong> {selectedPlace.types?.join(', ')}</div>
{selectedPlace.rating && (
<div><strong>Rating:</strong> {selectedPlace.rating} ⭐</div>
)}
{selectedPlace.price_level !== undefined && (
<div><strong>Price Level:</strong> {'$'.repeat(selectedPlace.price_level + 1)}</div>
)}
{selectedPlace.opening_hours && (
<div><strong>Open Now:</strong> {selectedPlace.opening_hours.open_now ? 'Yes' : 'No'}</div>
)}
{selectedPlace.international_phone_number && (
<div><strong>Phone:</strong> {selectedPlace.international_phone_number}</div>
)}
{selectedPlace.website && (
<div>
<strong>Website:</strong>
<a href={selectedPlace.website} target="_blank" rel="noopener noreferrer" style={{ marginLeft: '5px' }}>
{selectedPlace.website}
</a>
</div>
)}
{selectedPlace.photos && selectedPlace.photos.length > 0 && (
<div style={{ marginTop: '10px' }}>
<strong>Photos:</strong>
<div style={{ display: 'flex', gap: '10px', marginTop: '5px', overflow: 'auto' }}>
{selectedPlace.photos.slice(0, 3).map((photo, index) => (
<img
key={index}
src={photo.getUrl({ maxWidth: 200, maxHeight: 150 })}
alt={`${selectedPlace.name} photo ${index + 1}`}
style={{ borderRadius: '4px', flexShrink: 0 }}
/>
))}
</div>
</div>
)}
</div>
)}
<GoogleMap
center={
selectedPlace?.geometry?.location
? selectedPlace.geometry.location.toJSON()
: { lat: 40.7128, lng: -74.0060 }
}
zoom={selectedPlace ? 16 : 10}
mapContainerStyle={{ width: '100%', height: '400px' }}
>
{selectedPlace && selectedPlace.geometry && (
<Marker position={selectedPlace.geometry.location!.toJSON()} />
)}
</GoogleMap>
</div>
</LoadScript>
);
}
// Autocomplete with bounds restriction
function BoundedAutocomplete() {
const [mapCenter, setMapCenter] = useState({ lat: 40.7128, lng: -74.0060 });
const [mapBounds, setMapBounds] = useState<google.maps.LatLngBounds | null>(null);
const [selectedPlace, setSelectedPlace] = useState<google.maps.places.PlaceResult | null>(null);
const autocompleteRef = useRef<google.maps.places.Autocomplete | null>(null);
const mapRef = useRef<google.maps.Map | null>(null);
const onMapLoad = (map: google.maps.Map) => {
mapRef.current = map;
updateBounds(map);
};
const updateBounds = (map: google.maps.Map) => {
const bounds = map.getBounds();
if (bounds) {
setMapBounds(bounds);
}
};
const onPlaceChanged = () => {
if (autocompleteRef.current) {
const place = autocompleteRef.current.getPlace();
setSelectedPlace(place);
if (place.geometry && place.geometry.location) {
const location = place.geometry.location.toJSON();
setMapCenter(location);
// Fit map to place if it has a viewport
if (place.geometry.viewport && mapRef.current) {
mapRef.current.fitBounds(place.geometry.viewport);
}
}
}
};
return (
<LoadScript googleMapsApiKey="YOUR_API_KEY" libraries={['places']}>
<div>
<div style={{ padding: '10px', background: '#f0f0f0' }}>
<div style={{ marginBottom: '10px' }}>
<strong>Search within current map area:</strong>
</div>
<Autocomplete
onLoad={(autocomplete) => {
autocompleteRef.current = autocomplete;
}}
onPlaceChanged={onPlaceChanged}
bounds={mapBounds || undefined}
options={{
strictBounds: true // Restrict results to the specified bounds
}}
>
<input
type="text"
placeholder="Search places within map area"
style={{
width: '100%',
height: '40px',
padding: '0 15px',
fontSize: '16px',
border: '1px solid #ccc',
borderRadius: '4px'
}}
/>
</Autocomplete>
{selectedPlace && (
<div style={{ marginTop: '10px', padding: '10px', background: 'white', borderRadius: '4px' }}>
<strong>{selectedPlace.name}</strong>
<div>{selectedPlace.formatted_address}</div>
</div>
)}
</div>
<GoogleMap
center={mapCenter}
zoom={13}
mapContainerStyle={{ width: '100%', height: '400px' }}
onLoad={onMapLoad}
onBoundsChanged={() => {
if (mapRef.current) {
updateBounds(mapRef.current);
}
}}
>
{selectedPlace && selectedPlace.geometry && (
<Marker position={selectedPlace.geometry.location!.toJSON()} />
)}
</GoogleMap>
</div>
</LoadScript>
);
}Provides place search functionality without the autocomplete dropdown, useful for custom search interfaces.
/**
* Provides place search functionality without autocomplete dropdown
* Useful for custom search interfaces and batch place searching
*/
interface StandaloneSearchBoxProps {
children: React.ReactNode; // Required - must contain exactly one input element
bounds?: google.maps.LatLngBounds | google.maps.LatLngBoundsLiteral;
options?: google.maps.places.SearchBoxOptions;
// Event handlers
onPlacesChanged?: () => void;
// Lifecycle events
onLoad?: (searchBox: google.maps.places.SearchBox) => void;
onUnmount?: (searchBox: google.maps.places.SearchBox) => void;
}
interface google.maps.places.SearchBoxOptions {
bounds?: google.maps.LatLngBounds | google.maps.LatLngBoundsLiteral;
}
function StandaloneSearchBox(props: StandaloneSearchBoxProps): JSX.Element;Usage Examples:
import React, { useState, useRef } from 'react';
import { GoogleMap, LoadScript, StandaloneSearchBox, Marker } from '@react-google-maps/api';
// Basic search box
function BasicSearchBox() {
const [places, setPlaces] = useState<google.maps.places.PlaceResult[]>([]);
const [mapCenter, setMapCenter] = useState({ lat: 40.7128, lng: -74.0060 });
const searchBoxRef = useRef<google.maps.places.SearchBox | null>(null);
const onLoad = (searchBox: google.maps.places.SearchBox) => {
searchBoxRef.current = searchBox;
};
const onPlacesChanged = () => {
if (searchBoxRef.current) {
const places = searchBoxRef.current.getPlaces();
if (places && places.length > 0) {
setPlaces(places);
// Center map on first result
const firstPlace = places[0];
if (firstPlace.geometry && firstPlace.geometry.location) {
setMapCenter(firstPlace.geometry.location.toJSON());
}
console.log('Search results:', places);
}
}
};
return (
<LoadScript googleMapsApiKey="YOUR_API_KEY" libraries={['places']}>
<div>
<div style={{ padding: '10px' }}>
<StandaloneSearchBox onLoad={onLoad} onPlacesChanged={onPlacesChanged}>
<input
type="text"
placeholder="Search for places"
style={{
width: '100%',
height: '40px',
padding: '0 15px',
fontSize: '16px',
border: '2px solid #4285f4',
borderRadius: '8px',
outline: 'none'
}}
/>
</StandaloneSearchBox>
</div>
{places.length > 0 && (
<div style={{ padding: '10px', background: '#f0f0f0' }}>
<h4>Search Results ({places.length}):</h4>
<div style={{ maxHeight: '200px', overflowY: 'auto' }}>
{places.map((place, index) => (
<div key={index} style={{ padding: '5px', borderBottom: '1px solid #ccc' }}>
<strong>{place.name}</strong>
<div>{place.formatted_address}</div>
</div>
))}
</div>
</div>
)}
<GoogleMap
center={mapCenter}
zoom={15}
mapContainerStyle={{ width: '100%', height: '400px' }}
>
{places.map((place, index) =>
place.geometry && place.geometry.location && (
<Marker
key={index}
position={place.geometry.location.toJSON()}
title={place.name}
/>
)
)}
</GoogleMap>
</div>
</LoadScript>
);
}
// Advanced search with filtering and categorization
function AdvancedSearchBox() {
const [places, setPlaces] = useState<google.maps.places.PlaceResult[]>([]);
const [filteredPlaces, setFilteredPlaces] = useState<google.maps.places.PlaceResult[]>([]);
const [selectedCategory, setSelectedCategory] = useState<string>('all');
const [mapBounds, setMapBounds] = useState<google.maps.LatLngBounds | null>(null);
const searchBoxRef = useRef<google.maps.places.SearchBox | null>(null);
const mapRef = useRef<google.maps.Map | null>(null);
const categories = [
{ value: 'all', label: 'All Places' },
{ value: 'restaurant', label: 'Restaurants' },
{ value: 'lodging', label: 'Hotels' },
{ value: 'tourist_attraction', label: 'Attractions' },
{ value: 'store', label: 'Stores' },
{ value: 'bank', label: 'Banks' },
{ value: 'hospital', label: 'Hospitals' }
];
const onPlacesChanged = () => {
if (searchBoxRef.current) {
const searchResults = searchBoxRef.current.getPlaces();
if (searchResults && searchResults.length > 0) {
setPlaces(searchResults);
filterPlaces(searchResults, selectedCategory);
// Fit map to show all results
if (mapRef.current && searchResults.length > 1) {
const bounds = new google.maps.LatLngBounds();
searchResults.forEach(place => {
if (place.geometry && place.geometry.location) {
bounds.extend(place.geometry.location);
}
});
mapRef.current.fitBounds(bounds);
}
}
}
};
const filterPlaces = (placesToFilter: google.maps.places.PlaceResult[], category: string) => {
if (category === 'all') {
setFilteredPlaces(placesToFilter);
} else {
const filtered = placesToFilter.filter(place =>
place.types?.includes(category as any)
);
setFilteredPlaces(filtered);
}
};
React.useEffect(() => {
filterPlaces(places, selectedCategory);
}, [selectedCategory, places]);
const getMarkerColor = (place: google.maps.places.PlaceResult) => {
const types = place.types || [];
if (types.includes('restaurant' as any)) return 'red';
if (types.includes('lodging' as any)) return 'blue';
if (types.includes('tourist_attraction' as any)) return 'green';
if (types.includes('store' as any)) return 'orange';
return 'gray';
};
return (
<LoadScript googleMapsApiKey="YOUR_API_KEY" libraries={['places']}>
<div>
<div style={{ padding: '10px', background: '#f0f0f0' }}>
<div style={{ marginBottom: '10px' }}>
<StandaloneSearchBox
onLoad={(searchBox) => { searchBoxRef.current = searchBox; }}
onPlacesChanged={onPlacesChanged}
bounds={mapBounds || undefined}
>
<input
type="text"
placeholder="Search for restaurants, hotels, attractions..."
style={{
width: '100%',
height: '40px',
padding: '0 15px',
fontSize: '16px',
border: '1px solid #ccc',
borderRadius: '4px'
}}
/>
</StandaloneSearchBox>
</div>
<div style={{ marginBottom: '10px' }}>
<label>Filter by category: </label>
<select
value={selectedCategory}
onChange={(e) => setSelectedCategory(e.target.value)}
style={{ marginLeft: '10px', padding: '5px' }}
>
{categories.map(category => (
<option key={category.value} value={category.value}>
{category.label}
</option>
))}
</select>
</div>
{filteredPlaces.length > 0 && (
<div>
<div>Showing {filteredPlaces.length} of {places.length} places</div>
</div>
)}
</div>
{filteredPlaces.length > 0 && (
<div style={{
height: '150px',
overflowY: 'auto',
padding: '10px',
background: 'white',
borderTop: '1px solid #ccc'
}}>
{filteredPlaces.map((place, index) => (
<div
key={index}
style={{
padding: '8px',
borderBottom: '1px solid #eee',
cursor: 'pointer',
display: 'flex',
alignItems: 'center'
}}
onClick={() => {
if (place.geometry && place.geometry.location && mapRef.current) {
mapRef.current.setCenter(place.geometry.location);
mapRef.current.setZoom(16);
}
}}
>
<div
style={{
width: '12px',
height: '12px',
borderRadius: '50%',
backgroundColor: getMarkerColor(place),
marginRight: '10px',
flexShrink: 0
}}
/>
<div>
<div><strong>{place.name}</strong></div>
<div style={{ fontSize: '12px', color: '#666' }}>
{place.formatted_address}
</div>
{place.rating && (
<div style={{ fontSize: '12px', color: '#666' }}>
Rating: {place.rating} ⭐
</div>
)}
</div>
</div>
))}
</div>
)}
<GoogleMap
center={{ lat: 40.7128, lng: -74.0060 }}
zoom={13}
mapContainerStyle={{ width: '100%', height: '400px' }}
onLoad={(map) => {
mapRef.current = map;
}}
onBoundsChanged={() => {
if (mapRef.current) {
const bounds = mapRef.current.getBounds();
if (bounds) {
setMapBounds(bounds);
}
}
}}
>
{filteredPlaces.map((place, index) =>
place.geometry && place.geometry.location && (
<Marker
key={index}
position={place.geometry.location.toJSON()}
title={place.name}
icon={`https://maps.google.com/mapfiles/ms/icons/${getMarkerColor(place)}-dot.png`}
/>
)
)}
</GoogleMap>
</div>
</LoadScript>
);
}
// Search box with custom UI and place details
function CustomSearchInterface() {
const [places, setPlaces] = useState<google.maps.places.PlaceResult[]>([]);
const [selectedPlace, setSelectedPlace] = useState<google.maps.places.PlaceResult | null>(null);
const [searchValue, setSearchValue] = useState('');
const searchBoxRef = useRef<google.maps.places.SearchBox | null>(null);
const performSearch = () => {
if (searchBoxRef.current && searchValue.trim()) {
// Trigger search by setting the input value and firing the event
const input = document.getElementById('search-input') as HTMLInputElement;
if (input) {
input.value = searchValue;
google.maps.event.trigger(input, 'focus');
google.maps.event.trigger(input, 'keydown', { keyCode: 13 });
}
}
};
const onPlacesChanged = () => {
if (searchBoxRef.current) {
const results = searchBoxRef.current.getPlaces();
if (results && results.length > 0) {
setPlaces(results);
setSelectedPlace(results[0]); // Select first result by default
}
}
};
return (
<LoadScript googleMapsApiKey="YOUR_API_KEY" libraries={['places']}>
<div style={{ display: 'flex', height: '500px' }}>
{/* Search Panel */}
<div style={{ width: '350px', padding: '15px', background: '#f8f9fa', borderRight: '1px solid #dee2e6' }}>
<h3 style={{ margin: '0 0 15px 0' }}>Place Search</h3>
<div style={{ marginBottom: '15px' }}>
<StandaloneSearchBox
onLoad={(searchBox) => { searchBoxRef.current = searchBox; }}
onPlacesChanged={onPlacesChanged}
>
<input
id="search-input"
type="text"
value={searchValue}
onChange={(e) => setSearchValue(e.target.value)}
onKeyPress={(e) => e.key === 'Enter' && performSearch()}
placeholder="Search places..."
style={{
width: '100%',
height: '40px',
padding: '0 15px',
border: '1px solid #ced4da',
borderRadius: '4px',
fontSize: '14px'
}}
/>
</StandaloneSearchBox>
<button
onClick={performSearch}
style={{
width: '100%',
marginTop: '10px',
padding: '10px',
background: '#007bff',
color: 'white',
border: 'none',
borderRadius: '4px',
cursor: 'pointer'
}}
>
Search
</button>
</div>
{/* Results List */}
<div style={{ maxHeight: '300px', overflowY: 'auto' }}>
{places.map((place, index) => (
<div
key={index}
onClick={() => setSelectedPlace(place)}
style={{
padding: '10px',
margin: '5px 0',
background: selectedPlace === place ? '#e3f2fd' : 'white',
border: '1px solid #dee2e6',
borderRadius: '4px',
cursor: 'pointer'
}}
>
<div style={{ fontWeight: 'bold', marginBottom: '5px' }}>
{place.name}
</div>
<div style={{ fontSize: '12px', color: '#666' }}>
{place.formatted_address}
</div>
{place.rating && (
<div style={{ fontSize: '12px', marginTop: '5px' }}>
⭐ {place.rating} {place.user_ratings_total && `(${place.user_ratings_total} reviews)`}
</div>
)}
</div>
))}
</div>
{/* Selected Place Details */}
{selectedPlace && (
<div style={{
marginTop: '15px',
padding: '15px',
background: 'white',
border: '1px solid #dee2e6',
borderRadius: '4px'
}}>
<h4 style={{ margin: '0 0 10px 0' }}>Place Details</h4>
<div><strong>Name:</strong> {selectedPlace.name}</div>
<div><strong>Address:</strong> {selectedPlace.formatted_address}</div>
<div><strong>Types:</strong> {selectedPlace.types?.join(', ')}</div>
{selectedPlace.rating && (
<div><strong>Rating:</strong> {selectedPlace.rating} / 5</div>
)}
{selectedPlace.price_level !== undefined && (
<div><strong>Price:</strong> {'$'.repeat(selectedPlace.price_level + 1)}</div>
)}
</div>
)}
</div>
{/* Map */}
<div style={{ flex: 1 }}>
<GoogleMap
center={
selectedPlace?.geometry?.location
? selectedPlace.geometry.location.toJSON()
: { lat: 40.7128, lng: -74.0060 }
}
zoom={selectedPlace ? 16 : 12}
mapContainerStyle={{ width: '100%', height: '100%' }}
>
{places.map((place, index) =>
place.geometry && place.geometry.location && (
<Marker
key={index}
position={place.geometry.location.toJSON()}
title={place.name}
icon={{
url: selectedPlace === place
? 'https://maps.google.com/mapfiles/ms/icons/red-dot.png'
: 'https://maps.google.com/mapfiles/ms/icons/blue-dot.png',
scaledSize: new google.maps.Size(32, 32)
}}
onClick={() => setSelectedPlace(place)}
/>
)
)}
</GoogleMap>
</div>
</div>
</LoadScript>
);
}Guidelines for effective Places API usage and optimization strategies.
/**
* Places API integration best practices and optimization
*/
interface PlacesAPIBestPractices {
// Field selection optimization
requestOnlyNeededFields: boolean; // Reduce API costs by requesting specific fields
essentialFields: string[]; // Minimal required fields for basic functionality
extendedFields: string[]; // Additional fields for enhanced features
// Geographic optimization
useBounds: boolean; // Restrict searches to relevant geographic areas
useStrictBounds: boolean; // Enforce strict boundary compliance
// Performance optimization
implementDebouncing: boolean; // Debounce user input to reduce API calls
cacheResults: boolean; // Cache place results for repeated queries
useSessionTokens: boolean; // Group related requests for cost optimization
}
// Example field optimization
const PLACE_FIELDS = {
basic: ['place_id', 'name', 'formatted_address', 'geometry'],
contact: ['international_phone_number', 'website', 'opening_hours'],
atmosphere: ['rating', 'user_ratings_total', 'price_level', 'reviews'],
media: ['photos', 'icon', 'icon_background_color'],
detailed: ['types', 'vicinity', 'plus_code', 'url']
};
// Example debounced autocomplete implementation
const useDebouncedAutocomplete = (delay: number = 300) => {
const [searchTerm, setSearchTerm] = React.useState('');
const [debouncedTerm, setDebouncedTerm] = React.useState('');
React.useEffect(() => {
const timer = setTimeout(() => {
setDebouncedTerm(searchTerm);
}, delay);
return () => clearTimeout(timer);
}, [searchTerm, delay]);
return { searchTerm, setSearchTerm, debouncedTerm };
};Install with Tessl CLI
npx tessl i tessl/npm-react-google-maps--api