Uh oh!
There was an error while loading.Please reload this page.
- Notifications
You must be signed in to change notification settings - Fork65
😎 📍 React hook for Google Maps Places Autocomplete.
License
wellyshen/use-places-autocomplete
Folders and files
| Name | Name | Last commit message | Last commit date | |
|---|---|---|---|---|
Repository files navigation
👀 We're actively seeking dedicated maintainers for the repo — if you're interested, please contact me directly.
This is a Reacthook forGoogle Maps Places Autocomplete, which helps you build a UI component with the feature of place autocomplete easily! By leveraging the power ofGoogle Maps Places API, you can provide a great UX (user experience) for user interacts with your search bar or form, etc. Hope you guys 👍🏻 it.
❤️ it? ⭐️ it onGitHub orTweet about it.
⚡️ Try yourself:https://use-places-autocomplete.netlify.app
- 🧠 Provides intelligent places suggestions powered byGoogle Maps Places API.
- 🎣 Builds your own customized autocomplete UI byReact hook.
- 🔧Utility functions to do geocoding and get geographic coordinates usingGoogle Maps Geocoding API.
- 🤑 Built-incache mechanism for you to save the cost of Google APIs.
- 💰 Built-in debounce mechanism for you to lower the cost of Google APIs.
- 🚀 Supports asynchronous Google script loading.
- 📜 SupportsTypeScript type definition.
- ⌨️ Builds a UX-rich component (e.g.WAI-ARIA compliant and keyword support) via comprehensivedemo code.
- 🦔 Tiny size (~ 1.7KB gzipped). No external dependencies, aside from the
react.
To useuse-places-autocomplete, you must usereact@16.8.0 or greater which includes hooks.
This package is distributed vianpm.
$ yarn add use-places-autocomplete# or$ npm install --save use-places-autocompleteWhen working with TypeScript you need to install the@types/google.maps as adevDependencies.
$ yarn add --dev @types/google.maps# or$ npm install --save-dev @types/google.mapsusePlacesAutocomplete is based on thePlaces Autocomplete (or more specificdocs) ofGoogle Maps Place API. If you are unfamiliar with these APIs, we recommend you review them before we start.
To use this hook, there're two things we need to do:
Use thescript tag to load the library in your project and pass the value of thecallback parameter to thecallbackName option.
<scriptdefersrc="https://maps.googleapis.com/maps/api/js?key=YOUR_API_KEY&libraries=places&callback=YOUR_CALLBACK_NAME"></script>
⚠️ If you got a global function not found error. Make sureusePlaceAutocompleteis declared before the script was loaded. You can use theasync or defer attribute of the<script>element to achieve that.
Now we can start to build our component. Check theAPI out to learn more.
importusePlacesAutocomplete,{getGeocode,getLatLng,}from"use-places-autocomplete";importuseOnclickOutsidefrom"react-cool-onclickoutside";constPlacesAutocomplete=()=>{const{ ready, value,suggestions:{ status, data}, setValue, clearSuggestions,}=usePlacesAutocomplete({callbackName:"YOUR_CALLBACK_NAME",requestOptions:{/* Define search scope here */},debounce:300,});constref=useOnclickOutside(()=>{// When the user clicks outside of the component, we can dismiss// the searched suggestions by calling this methodclearSuggestions();});consthandleInput=(e)=>{// Update the keyword of the input elementsetValue(e.target.value);};consthandleSelect=({ description})=>()=>{// When the user selects a place, we can replace the keyword without request data from API// by setting the second parameter to "false"setValue(description,false);clearSuggestions();// Get latitude and longitude via utility functionsgetGeocode({address:description}).then((results)=>{const{ lat, lng}=getLatLng(results[0]);console.log("📍 Coordinates: ",{ lat, lng});});};constrenderSuggestions=()=>data.map((suggestion)=>{const{ place_id,structured_formatting:{ main_text, secondary_text},}=suggestion;return(<likey={place_id}onClick={handleSelect(suggestion)}><strong>{main_text}</strong><small>{secondary_text}</small></li>);});return(<divref={ref}><inputvalue={value}onChange={handleInput}disabled={!ready}placeholder="Where are you going?"/>{/* We can use the "status" to decide whether we should display the dropdown or not */}{status==="OK"&&<ul>{renderSuggestions()}</ul>}</div>);};
💡react-cool-onclickoutside is my other hook library, which can help you handle the interaction of user clicks outside of the component(s).
Easy right? This is the magic ofusePlacesAutocomplete ✨. I just showed you how it works via a minimal example. However, you can build a UX rich autocomplete component, likeWAI-ARIA compliant and keyword interaction like mydemo, by checking thecode or integrating this hook with thecombobox ofReach UI to achieve that.
importusePlacesAutocompletefrom"use-places-autocomplete";import{Combobox,ComboboxInput,ComboboxPopover,ComboboxList,ComboboxOption,}from"@reach/combobox";import"@reach/combobox/styles.css";constPlacesAutocomplete=()=>{const{ ready, value,suggestions:{ status, data}, setValue,}=usePlacesAutocomplete({callbackName:"YOUR_CALLBACK_NAME"});consthandleInput=(e)=>{setValue(e.target.value);};consthandleSelect=(val)=>{setValue(val,false);};return(<ComboboxonSelect={handleSelect}aria-labelledby="demo"><ComboboxInputvalue={value}onChange={handleInput}disabled={!ready}/><ComboboxPopover><ComboboxList>{status==="OK"&&data.map(({ place_id, description})=>(<ComboboxOptionkey={place_id}value={description}/>))}</ComboboxList></ComboboxPopover></Combobox>);};
When loading the Google Maps Places API via a 3rd-party library, you may need to wait for the script to be ready before using this hook. However, you can lazily initialize the hook in the following ways, depending on your use case.
importusePlacesAutocompletefrom"use-places-autocomplete";constApp=()=>{const{ init}=usePlacesAutocomplete({initOnMount:false,// Disable initializing when the component mounts, default is true});const[loading]=useGoogleMapsApi({library:"places",onLoad:()=>init(),// Lazily initializing the hook when the script is ready});return<div>{/* Some components... */}</div>;};
importusePlacesAutocompletefrom"use-places-autocomplete";constPlacesAutocomplete=()=>{const{ ready, value, suggestions, setValue}=usePlacesAutocomplete();return<div>{/* Some components... */}</div>;};constApp=()=>{const[loading]=useGoogleMapsApi({library:"places"});return(<div>{!loading ?<PlacesAutocomplete/> :null}{/* Other components... */}</div>);};
By default, this library caches the response data to help you save thecost of Google Maps Places API and optimize search performance.
constmethods=usePlacesAutocomplete({// Provide the cache time in seconds, the default is 24 hourscache:24*60*60,});
By the way, the cached data is stored via theWindow.sessionStorage API.
You may need to have multiple caches. For example, if you use differentplace type restrictions for different pickers in your app.
constmethods=usePlacesAutocomplete({// Provide a custom cache keycacheKey:"region-restricted",});
Note that usePlacesAutocomplete will prefix this with
upa-, so the above would becomeupa-region-restrictedin sessionStorage.
constreturnObj=usePlacesAutocomplete(parameterObj);
When usingusePlacesAutocomplete, you can configure the following options via the parameter.
| Key | Type | Default | Description |
|---|---|---|---|
requestOptions | object | Therequest options of Google Maps Places API except forinput (e.g. bounds, radius, etc.). | |
googleMaps | object | window.google.maps | In case you want to provide your own Google Maps object, pass thegoogle.maps to it. |
callbackName | string | The value of thecallback parameter whenloading the Google Maps JavaScript library. | |
debounce | number | 200 | Number of milliseconds to delay before making a request to Google Maps Places API. |
cache | number | false | 86400 (24 hours) | Number of seconds tocache the response data of Google Maps Places API. |
cacheKey | string | "upa" | Optional cache key so one can use multiple caches if needed. |
defaultValue | string | "" | Default value for theinput element. |
initOnMount | boolean | true | Initialize the hook with Google Maps Places API when the component mounts. |
It's returned with the following properties.
| Key | Type | Default | Description |
|---|---|---|---|
ready | boolean | false | The ready status ofusePlacesAutocomplete. |
value | string | "" | value for the input element. |
suggestions | object | { loading: false, status: "", data: [] } | Seesuggestions. |
setValue | function | (value, shouldFetchData = true) => {} | SeesetValue. |
clearSuggestions | function | SeeclearSuggestions. | |
clearCache | function | (key = cacheKey) => {} | Clears thecached data. |
init | function | Useful whenlazily initializing the hook. |
The search result of Google Maps Places API, which contains the following properties:
loading: boolean- indicates the status of a request is pending or has been completed. It's useful for displaying a loading indicator for the user.status: string- indicates the status of the API response, which has thesevalues. It's useful to decide whether we should display the dropdown or not.data: array- an array of suggestion objects each contains all thedata.
Set thevalue of the input element. Use the case below.
importusePlacesAutocompletefrom"use-places-autocomplete";constPlacesAutocomplete=()=>{const{ value, setValue}=usePlacesAutocomplete();consthandleInput=(e)=>{// Place a "string" to update the value of the input elementsetValue(e.target.value);};return(<div><inputvalue={value}onChange={handleInput}/>{/* Render dropdown */}</div>);};
In addition, thesetValue method has an extra parameter, which can be used to disable hitting Google Maps Places API.
importusePlacesAutocompletefrom"use-places-autocomplete";constPlacesAutocomplete=()=>{const{ value,suggestions:{ status, data}, setValue,}=usePlacesAutocomplete();consthandleSelect=({ description})=>()=>{// When the user selects a place, we can replace the keyword without requesting data from the API// by setting the second parameter to "false"setValue(description,false);};constrenderSuggestions=()=>data.map((suggestion)=>(<likey={suggestion.place_id}onClick={handleSelect(suggestion)}>{/* Render suggestion text */}</li>));return(<div><inputvalue={value}onChange={handleInput}/>{status==="OK"&&<ul>{renderSuggestions()}</ul>}</div>);};
Calling the method will clear and reset all the properties of thesuggestions object to default. It's useful for dismissing the dropdown.
importusePlacesAutocompletefrom"use-places-autocomplete";importuseOnclickOutsidefrom"react-cool-onclickoutside";constPlacesAutocomplete=()=>{const{ value,suggestions:{ status, data}, setValue, clearSuggestions,}=usePlacesAutocomplete();constref=useOnclickOutside(()=>{// When the user clicks outside of the component, call it to clear and reset the suggestions dataclearSuggestions();});constrenderSuggestions=()=>data.map((suggestion)=>(<likey={suggestion.place_id}onClick={handleSelect(suggestion)}>{/* Render suggestion text */}</li>));return(<divref={ref}><inputvalue={value}onChange={handleInput}/>{/* After calling the clearSuggestions(), the "status" is reset so the dropdown is hidden */}{status==="OK"&&<ul>{renderSuggestions()}</ul>}</div>);};
We providegetGeocode,getLatLng,getZipCode, andgetDetails utils for you to do geocoding and get geographic coordinates when needed.
It helps you convert address (e.g. "Section 5, Xinyi Road, Xinyi District, Taipei City, Taiwan") into geographic coordinates (e.g. latitude 25.033976 and longitude 121.5645389), or restrict the results to a specific area byGoogle Maps Geocoding API.
In case you want to restrict the results to a specific area, you will have to pass theaddress and thecomponentRestrictions matching theGeocoderComponentRestrictions interface.
import{getGeocode}from"use-places-autocomplete";constparameter={address:"Section 5, Xinyi Road, Xinyi District, Taipei City, Taiwan",// orplaceId:"ChIJraeA2rarQjQRPBBjyR3RxKw",};getGeocode(parameter).then((results)=>{console.log("Geocoding results: ",results);}).catch((error)=>{console.log("Error: ",error);});
getGeocode is an asynchronous function with the following API:
parameter: object- you must supply one, only one ofaddressorlocationorplaceIdand optionallybounds,componentRestrictions,region. It'll be passed asGeocoding Requests.results: array- an array of objects each contains all thedata.error: string- the error status of API response, which has thesevalues (except for "OK").
It helps you get thelat andlng from the result object ofgetGeocode.
import{getGeocode,getLatLng}from"use-places-autocomplete";constparameter={address:"Section 5, Xinyi Road, Xinyi District, Taipei City, Taiwan",};getGeocode(parameter).then((results)=>{const{ lat, lng}=getLatLng(results[0]);console.log("Coordinates: ",{ lat, lng});});
getLatLng is a function with the following API:
parameter: object- the result object ofgetGeocode.latLng: object- contains the latitude and longitude properties.error: any- an exception.
It helps you get thepostal_code from the result object ofgetGeocode.
import{getGeocode,getZipCode}from"use-places-autocomplete";constparameter={address:"Section 5, Xinyi Road, Xinyi District, Taipei City, Taiwan",};getGeocode(parameter)// By default we use the "long_name" value from API response, you can tell the utility to use "short_name"// by setting the second parameter to "true".then((results)=>{constzipCode=getZipCode(results[0],false);console.log("ZIP Code: ",zipCode);});
getZipCode is a function with the following API:
parameters- there're two parameters:1st: object- the result object ofgetGeocode.2nd: boolean- should use theshort_nameor not fromAPI response, default isfalse.
zipCode: string | null- the zip code. If the address doesn't have a zip code it will benull.error: any- an exception.
Retrieves a great deal of information about a particular place ID (suggestion).
importusePlacesAutocomplete,{getDetails}from"use-places-autocomplete";constPlacesAutocomplete=()=>{const{ suggestions, value, setValue}=usePlacesAutocomplete();consthandleInput=(e)=>{// Place a "string" to update the value of the input elementsetValue(e.target.value);};constsubmit=()=>{constparameter={// Use the "place_id" of suggestion from the dropdown (object), here just taking the first suggestion for brevityplaceId:suggestions[0].place_id,// Specify the return data that you want (optional)fields:["name","rating"],};getDetails(parameter).then((details)=>{console.log("Details: ",details);}).catch((error)=>{console.log("Error: ",error);});};return(<div><inputvalue={value}onChange={handleInput}/>{/* Render dropdown */}<buttononClick={submit}>Submit Suggestion</button></div>);};
getDetails is an asynchronous function with the following API:
parameter: object-the request of the PlacesService'sgetDetails()method. You must supply theplaceIdthat you would like details about. If you do not specify any fields or omit the fields parameter you will get every field available.placeResult: object | null-the details about the specific place your queried.error: any- an exception.
⚠️ warning, you are billed based on how much information you retrieve, So it is advised that you retrieve just what you need.
💡 If you have written any blog post or article about
use-places-autocomplete, please open a PR to add it here.
- Featured onReact Status #175.
- Featured onReact Newsletter #199.
Thanks goes to these wonderful people (emoji key):
Welly 💻📖🚧 | Kyle 🌍 | Lazar Karić 💻📖 | Raif Harik 💻📖🤔 | Xerxes Jarquin 🐛 | Lucas O'Connell 📖 | Keven Jesus 🐛 |
Vinicius Uehara 📖 | Damon 💻 | Matthew Marcus 💻 | Chris Sandvik 💻 | Thomas Yee 📖 |
This project follows theall-contributors specification. Contributions of any kind are welcome!
About
😎 📍 React hook for Google Maps Places Autocomplete.
Topics
Resources
License
Code of conduct
Contributing
Security policy
Uh oh!
There was an error while loading.Please reload this page.
Stars
Watchers
Forks
Sponsor this project
Uh oh!
There was an error while loading.Please reload this page.
