@georgemunyoro/use-places-autocomplete
Version:
React hook for Google Maps Places Autocomplete.
303 lines (257 loc) • 8.74 kB
JavaScript
import { useRef, useState, useCallback, useEffect } from 'react';
function _extends() {
_extends = Object.assign || function (target) {
for (var i = 1; i < arguments.length; i++) {
var source = arguments[i];
for (var key in source) {
if (Object.prototype.hasOwnProperty.call(source, key)) {
target[key] = source[key];
}
}
}
return target;
};
return _extends.apply(this, arguments);
}
var useLatest = (function (val) {
var ref = useRef(val);
ref.current = val;
return ref;
});
var _debounce = (function (fn, delay) {
var timer; // eslint-disable-next-line func-names
return function () {
var _this = this;
for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
args[_key] = arguments[_key];
}
if (timer !== null) {
clearTimeout(timer);
timer = null;
}
timer = setTimeout(function () {
return fn.apply(_this, args);
}, delay);
};
});
function useUncontrolled(_ref) {
var value = _ref.value,
defaultValue = _ref.defaultValue,
finalValue = _ref.finalValue,
_ref$onChange = _ref.onChange,
onChange = _ref$onChange === void 0 ? function () {} : _ref$onChange;
var _useState = useState(defaultValue !== undefined ? defaultValue : finalValue),
uncontrolledValue = _useState[0],
setUncontrolledValue = _useState[1];
var handleUncontrolledChange = function handleUncontrolledChange(val) {
setUncontrolledValue(val);
for (var _len = arguments.length, payload = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
payload[_key - 1] = arguments[_key];
}
onChange == null ? void 0 : onChange.apply(void 0, [val].concat(payload));
};
if (value !== undefined) {
return [value, onChange, true];
}
return [uncontrolledValue, handleUncontrolledChange, false];
}
var loadApiErr = "💡 use-places-autocomplete: Google Maps Places API library must be loaded. See: https://github.com/wellyshen/use-places-autocomplete#load-the-library";
var usePlacesAutocomplete = function usePlacesAutocomplete(_temp) {
var _ref = _temp === void 0 ? {} : _temp,
requestOptions = _ref.requestOptions,
_ref$debounce = _ref.debounce,
debounce = _ref$debounce === void 0 ? 200 : _ref$debounce,
_ref$cache = _ref.cache,
cache = _ref$cache === void 0 ? 24 * 60 * 60 : _ref$cache,
_ref$cacheKey = _ref.cacheKey,
cacheKey = _ref$cacheKey === void 0 ? "upa" : _ref$cacheKey,
googleMaps = _ref.googleMaps,
callbackName = _ref.callbackName,
value = _ref.value,
_ref$defaultValue = _ref.defaultValue,
defaultValue = _ref$defaultValue === void 0 ? "" : _ref$defaultValue,
onChange = _ref.onChange,
_ref$initOnMount = _ref.initOnMount,
initOnMount = _ref$initOnMount === void 0 ? true : _ref$initOnMount;
var _useState = useState(false),
ready = _useState[0],
setReady = _useState[1];
var _useUncontrolled = useUncontrolled({
value: value,
defaultValue: defaultValue,
finalValue: "",
onChange: onChange
}),
_value = _useUncontrolled[0],
handleChange = _useUncontrolled[1];
var _useState2 = useState({
loading: false,
status: "",
data: []
}),
suggestions = _useState2[0],
setSuggestions = _useState2[1];
var asRef = useRef();
var requestOptionsRef = useLatest(requestOptions);
var googleMapsRef = useLatest(googleMaps);
var init = useCallback(function () {
var _google$maps;
if (asRef.current) return;
var _window = window,
google = _window.google;
var gMaps = googleMapsRef.current;
var placesLib = (gMaps == null ? void 0 : gMaps.places) || (google == null ? void 0 : (_google$maps = google.maps) == null ? void 0 : _google$maps.places);
if (!placesLib) {
console.error(loadApiErr);
return;
}
asRef.current = new placesLib.AutocompleteService();
setReady(true);
}, [googleMapsRef]);
var clearSuggestions = useCallback(function () {
setSuggestions({
loading: false,
status: "",
data: []
});
}, []);
var clearCache = useCallback(function (key) {
if (key === void 0) {
key = cacheKey;
}
try {
sessionStorage.removeItem(key);
} catch (error) {// Skip exception
}
}, [cacheKey]); // eslint-disable-next-line react-hooks/exhaustive-deps
var fetchPredictions = useCallback(_debounce(function (val) {
var _asRef$current;
if (!val) {
clearSuggestions();
return;
}
setSuggestions(function (prevState) {
return _extends({}, prevState, {
loading: true
});
});
var cachedData = {};
try {
cachedData = JSON.parse(sessionStorage.getItem(cacheKey) || "{}");
} catch (error) {// Skip exception
}
if (cache) {
cachedData = Object.keys(cachedData).reduce(function (acc, key) {
if (cachedData[key].maxAge - Date.now() >= 0) acc[key] = cachedData[key];
return acc;
}, {});
if (cachedData[val]) {
setSuggestions({
loading: false,
status: "OK",
data: cachedData[val].data
});
return;
}
}
(_asRef$current = asRef.current) == null ? void 0 : _asRef$current.getPlacePredictions(_extends({}, requestOptionsRef.current, {
input: val
}), function (data, status) {
setSuggestions({
loading: false,
status: status,
data: data || []
});
if (cache && status === "OK") {
cachedData[val] = {
data: data,
maxAge: Date.now() + cache * 1000
};
try {
sessionStorage.setItem(cacheKey, JSON.stringify(cachedData));
} catch (error) {// Skip exception
}
}
});
}, debounce), [cache, cacheKey, clearSuggestions, requestOptionsRef]);
var setValue = useCallback(function (val, shouldFetchData) {
if (shouldFetchData === void 0) {
shouldFetchData = true;
}
handleChange(val);
if (asRef.current && shouldFetchData) fetchPredictions(val);
}, [fetchPredictions, handleChange]);
useEffect(function () {
if (!initOnMount) return function () {
return null;
};
var _window2 = window,
google = _window2.google;
if (!googleMapsRef.current && !(google != null && google.maps) && callbackName) {
window[callbackName] = init;
} else {
init();
}
return function () {
// @ts-ignore
if (window[callbackName]) delete window[callbackName];
};
}, [callbackName, googleMapsRef, init, initOnMount]);
return {
ready: ready,
value: _value,
suggestions: suggestions,
setValue: setValue,
clearSuggestions: clearSuggestions,
clearCache: clearCache,
init: init
};
};
/* eslint-disable compat/compat */
var geocodeErr = "💡 use-places-autocomplete: Please provide an address when using getGeocode() with the componentRestrictions.";
var getGeocode = function getGeocode(args) {
var geocoder = new window.google.maps.Geocoder();
return new Promise(function (resolve, reject) {
geocoder.geocode(args, function (results, status) {
if (status !== "OK") reject(status);
if (!args.address && args.componentRestrictions) {
console.error(geocodeErr);
resolve(results);
}
resolve(results);
});
});
};
var getLatLng = function getLatLng(result) {
var _result$geometry$loca = result.geometry.location,
lat = _result$geometry$loca.lat,
lng = _result$geometry$loca.lng;
return {
lat: lat(),
lng: lng()
};
};
var getZipCode = function getZipCode(result, useShortName) {
var foundZip = result.address_components.find(function (_ref) {
var types = _ref.types;
return types.includes("postal_code");
});
if (!foundZip) return undefined;
return useShortName ? foundZip.short_name : foundZip.long_name;
};
var getDetailsErr = "💡 use-places-autocomplete: Please provide a place Id when using getDetails() either as a string or as part of an Autocomplete Prediction.";
var getDetails = function getDetails(args) {
var PlacesService = new window.google.maps.places.PlacesService(document.createElement("div"));
if (!args.placeId) {
console.error(getDetailsErr);
return Promise.reject(getDetailsErr);
}
return new Promise(function (resolve, reject) {
PlacesService.getDetails(args, function (results, status) {
if (status !== "OK") reject(status);
resolve(results);
});
});
};
export { usePlacesAutocomplete as default, getDetails, getGeocode, getLatLng, getZipCode };
//# sourceMappingURL=index.esm.js.map