diff --git a/lib/usePlacesAutocompleteService.js b/lib/usePlacesAutocompleteService.js index 8bcd820..a3adcb9 100644 --- a/lib/usePlacesAutocompleteService.js +++ b/lib/usePlacesAutocompleteService.js @@ -11,8 +11,6 @@ var _lodash = _interopRequireDefault(require("lodash.debounce")); var _utils = require("./utils"); -var _constants = require("./constants"); - function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) { symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); } keys.push.apply(keys, symbols); } return keys; } @@ -35,16 +33,12 @@ function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; } function usePlacesAutocompleteService(_ref) { var apiKey = _ref.apiKey, - _ref$googleMapsScript = _ref.googleMapsScriptBaseUrl, - googleMapsScriptBaseUrl = _ref$googleMapsScript === void 0 ? _constants.GOOGLE_MAP_SCRIPT_BASE_URL : _ref$googleMapsScript, _ref$debounce = _ref.debounce, debounce = _ref$debounce === void 0 ? 300 : _ref$debounce, _ref$options = _ref.options, options = _ref$options === void 0 ? {} : _ref$options, sessionToken = _ref.sessionToken, language = _ref.language; - var languageQueryParam = language ? "&language=".concat(language) : ""; - var googleMapsScriptUrl = "".concat(googleMapsScriptBaseUrl, "?key=").concat(apiKey, "&libraries=places").concat(languageQueryParam); var _useState = (0, _react.useState)([]), _useState2 = _slicedToArray(_useState, 2), @@ -80,8 +74,13 @@ function usePlacesAutocompleteService(_ref) { var placesService = (0, _react.useRef)(null); var autocompleteSession = (0, _react.useRef)(null); var handleLoadScript = (0, _react.useCallback)(function () { - return (0, _utils.loadGoogleMapScript)(googleMapsScriptBaseUrl, googleMapsScriptUrl); - }, [googleMapsScriptBaseUrl, googleMapsScriptUrl]); + return (0, _utils.loadGoogleMapScript)({ + apiKey: apiKey, + version: "weekly", + libraries: ["places"], + language: language + }); + }, [apiKey, language]); var debouncedPlacePredictions = (0, _react.useCallback)((0, _lodash.default)(function (optionsArg) { if (placesAutocompleteService.current && optionsArg.input) placesAutocompleteService.current.getPlacePredictions(_objectSpread(_objectSpread(_objectSpread({}, sessionToken && autocompleteSession.current ? { sessionToken: autocompleteSession.current @@ -101,20 +100,18 @@ function usePlacesAutocompleteService(_ref) { (0, _react.useEffect)(function () { if (!_utils.isBrowser) return; - var buildService = function buildService() { - // eslint-disable-next-line no-undef - if (!google) return console.error("Google has not been found. Make sure your provide apiKey prop."); // eslint-disable-next-line no-undef + var buildService = function buildService(service) { + console.log(service.maps.places, "service.maps.places"); + console.log(service.maps, "service.maps"); // eslint-disable-next-line no-undef - placesAutocompleteService.current = new google.maps.places.AutocompleteService(); // eslint-disable-next-line no-undef + placesAutocompleteService.current = new (service || google).maps.places.AutocompleteService(); // eslint-disable-next-line no-undef - placesService.current = new google.maps.places.PlacesService(document.createElement("div")); - if (sessionToken) autocompleteSession.current = new google.maps.places.AutocompleteSessionToken(); + placesService.current = new (service || google).maps.places.PlacesService(document.createElement("div")); + if (sessionToken) autocompleteSession.current = new (service || google).maps.places.AutocompleteSessionToken(); }; if (apiKey) { - handleLoadScript().then(function () { - return buildService(); - }); + handleLoadScript().then(buildService); } else { buildService(); } diff --git a/lib/usePlacesWidget.js b/lib/usePlacesWidget.js index 6378bf6..098e779 100644 --- a/lib/usePlacesWidget.js +++ b/lib/usePlacesWidget.js @@ -9,8 +9,6 @@ var _react = require("react"); var _utils = require("./utils"); -var _constants = require("./constants"); - function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) { symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); } keys.push.apply(keys, symbols); } return keys; } function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(Object(source), true).forEach(function (key) { _defineProperty(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; } @@ -37,19 +35,20 @@ function usePlacesWidget(props) { fields = _props$options$fields === void 0 ? ["address_components", "geometry.location", "place_id", "formatted_address"] : _props$options$fields, bounds = _props$options.bounds, options = _objectWithoutProperties(_props$options, ["types", "componentRestrictions", "fields", "bounds"]), - _props$googleMapsScri = props.googleMapsScriptBaseUrl, - googleMapsScriptBaseUrl = _props$googleMapsScri === void 0 ? _constants.GOOGLE_MAP_SCRIPT_BASE_URL : _props$googleMapsScri, language = props.language; var inputRef = (0, _react.useRef)(null); var event = (0, _react.useRef)(null); var autocompleteRef = (0, _react.useRef)(null); var observerHack = (0, _react.useRef)(null); - var languageQueryParam = language ? "&language=".concat(language) : ""; - var googleMapsScriptUrl = "".concat(googleMapsScriptBaseUrl, "?libraries=places&key=").concat(apiKey).concat(languageQueryParam); var handleLoadScript = (0, _react.useCallback)(function () { - return (0, _utils.loadGoogleMapScript)(googleMapsScriptBaseUrl, googleMapsScriptUrl); - }, [googleMapsScriptBaseUrl, googleMapsScriptUrl]); + return (0, _utils.loadGoogleMapScript)({ + apiKey: apiKey, + version: "weekly", + libraries: ["places"], + language: language + }); + }, []); (0, _react.useEffect)(function () { var config = _objectSpread(_objectSpread({}, options), {}, { fields: fields, @@ -64,11 +63,10 @@ function usePlacesWidget(props) { if (autocompleteRef.current || !inputRef.current || !_utils.isBrowser) return; if (ref && !ref.current) ref.current = inputRef.current; - var handleAutoComplete = function handleAutoComplete() { - if (typeof google === "undefined") return console.error("Google has not been found. Make sure your provide apiKey prop."); - if (!google.maps.places) return console.error("Google maps places API must be loaded."); + var handleAutoComplete = function handleAutoComplete(service) { + if (apiKey && !service.maps.places || !apiKey && !google.maps.places) return console.error("Google maps places API must be loaded."); if (!inputRef.current instanceof HTMLInputElement) return console.error("Input ref must be HTMLInputElement."); - autocompleteRef.current = new google.maps.places.Autocomplete(inputRef.current, config); + autocompleteRef.current = new (service || google).maps.places.Autocomplete(inputRef.current, config); event.current = autocompleteRef.current.addListener("place_changed", function () { if (onPlaceSelected && autocompleteRef && autocompleteRef.current) { onPlaceSelected(autocompleteRef.current.getPlace(), inputRef.current, autocompleteRef.current); @@ -77,9 +75,7 @@ function usePlacesWidget(props) { }; if (apiKey) { - handleLoadScript().then(function () { - return handleAutoComplete(); - }); + handleLoadScript().then(handleAutoComplete); } else { handleAutoComplete(); } diff --git a/lib/utils.js b/lib/utils.js index 947f3ae..596253d 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -4,37 +4,16 @@ Object.defineProperty(exports, "__esModule", { value: true }); exports.loadGoogleMapScript = exports.isBrowser = void 0; + +var _jsApiLoader = require("@googlemaps/js-api-loader"); + var isBrowser = typeof window !== "undefined" && window.document; exports.isBrowser = isBrowser; -var loadGoogleMapScript = function loadGoogleMapScript(googleMapsScriptBaseUrl, googleMapsScriptUrl) { +var loadGoogleMapScript = function loadGoogleMapScript(options) { if (!isBrowser) return Promise.resolve(); - - if (typeof google !== "undefined") { - if (google.maps && google.maps.api) return Promise.resolve(); - } - - var scriptElements = document.querySelectorAll("script[src*=\"".concat(googleMapsScriptBaseUrl, "\"")); - - if (scriptElements && scriptElements.length) { - return new Promise(function (resolve) { - // in case we already have a script on the page and it's loaded we resolve - if (typeof google !== "undefined") return resolve(); // otherwise we wait until it's loaded and resolve - - scriptElements[0].addEventListener("load", function () { - return resolve(); - }); - }); - } - - var el = document.createElement("script"); - el.src = googleMapsScriptUrl; - document.body.appendChild(el); - return new Promise(function (resolve) { - el.addEventListener("load", function () { - return resolve(); - }); - }); + var loader = new _jsApiLoader.Loader(options); + return loader.load(); }; exports.loadGoogleMapScript = loadGoogleMapScript; \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 1f2aafe..9407361 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "react-google-autocomplete", - "version": "2.3.0", + "version": "2.4.0", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -1228,6 +1228,14 @@ "to-fast-properties": "^2.0.0" } }, + "@googlemaps/js-api-loader": { + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/@googlemaps/js-api-loader/-/js-api-loader-1.12.1.tgz", + "integrity": "sha512-Nmk7pKn/819zIC0QTE5eA+kNmYpODQXyoCdk17nWiqx42gcb6NQEwssAuH/Aa+HeF0+ICYf5khIxDyY5njWJdg==", + "requires": { + "fast-deep-equal": "^3.1.3" + } + }, "@nicolo-ribaudo/chokidar-2": { "version": "2.1.8-no-fsevents", "resolved": "https://registry.npmjs.org/@nicolo-ribaudo/chokidar-2/-/chokidar-2-2.1.8-no-fsevents.tgz", @@ -2259,6 +2267,11 @@ } } }, + "fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" + }, "fbjs": { "version": "0.8.16", "resolved": "https://registry.npmjs.org/fbjs/-/fbjs-0.8.16.tgz", diff --git a/package.json b/package.json index 65da81b..f5b7d58 100644 --- a/package.json +++ b/package.json @@ -40,6 +40,7 @@ }, "homepage": "https://github.com/ErrorPro/react-google-autocomplete#readme", "dependencies": { + "@googlemaps/js-api-loader": "^1.12.1", "lodash.debounce": "^4.0.8", "prop-types": "^15.5.0" }, diff --git a/src/constants.js b/src/constants.js deleted file mode 100644 index 8952dd1..0000000 --- a/src/constants.js +++ /dev/null @@ -1,2 +0,0 @@ -export const GOOGLE_MAP_SCRIPT_BASE_URL = - "https://maps.googleapis.com/maps/api/js"; diff --git a/src/usePlacesAutocompleteService.js b/src/usePlacesAutocompleteService.js index 3f03b03..456e0d9 100644 --- a/src/usePlacesAutocompleteService.js +++ b/src/usePlacesAutocompleteService.js @@ -2,18 +2,14 @@ import { useEffect, useCallback, useRef, useState } from "react"; import debounceFn from "lodash.debounce"; import { isBrowser, loadGoogleMapScript } from "./utils"; -import { GOOGLE_MAP_SCRIPT_BASE_URL } from "./constants"; export default function usePlacesAutocompleteService({ apiKey, - googleMapsScriptBaseUrl = GOOGLE_MAP_SCRIPT_BASE_URL, debounce = 300, options = {}, sessionToken, language, }) { - const languageQueryParam = language ? `&language=${language}` : ""; - const googleMapsScriptUrl = `${googleMapsScriptBaseUrl}?key=${apiKey}&libraries=places${languageQueryParam}`; const [placePredictions, setPlacePredictions] = useState([]); const [isPlacePredsLoading, setIsPlacePredsLoading] = useState(false); const [placeInputValue, setPlaceInputValue] = useState(null); @@ -24,8 +20,14 @@ export default function usePlacesAutocompleteService({ const placesService = useRef(null); const autocompleteSession = useRef(null); const handleLoadScript = useCallback( - () => loadGoogleMapScript(googleMapsScriptBaseUrl, googleMapsScriptUrl), - [googleMapsScriptBaseUrl, googleMapsScriptUrl] + () => + loadGoogleMapScript({ + apiKey, + version: "weekly", + libraries: ["places"], + language, + }), + [apiKey, language] ); const debouncedPlacePredictions = useCallback( @@ -71,29 +73,28 @@ export default function usePlacesAutocompleteService({ useEffect(() => { if (!isBrowser) return; - const buildService = () => { - // eslint-disable-next-line no-undef - if (!google) - return console.error( - "Google has not been found. Make sure your provide apiKey prop." - ); + const buildService = (service) => { + console.log(service.maps.places, "service.maps.places"); + console.log(service.maps, "service.maps"); // eslint-disable-next-line no-undef - placesAutocompleteService.current = - new google.maps.places.AutocompleteService(); + placesAutocompleteService.current = new ( + service || google + ).maps.places.AutocompleteService(); // eslint-disable-next-line no-undef - placesService.current = new google.maps.places.PlacesService( + placesService.current = new (service || google).maps.places.PlacesService( document.createElement("div") ); if (sessionToken) - autocompleteSession.current = - new google.maps.places.AutocompleteSessionToken(); + autocompleteSession.current = new ( + service || google + ).maps.places.AutocompleteSessionToken(); }; if (apiKey) { - handleLoadScript().then(() => buildService()); + handleLoadScript().then(buildService); } else { buildService(); } diff --git a/src/usePlacesWidget.js b/src/usePlacesWidget.js index 64305db..ae8ff53 100644 --- a/src/usePlacesWidget.js +++ b/src/usePlacesWidget.js @@ -1,7 +1,6 @@ import { useEffect, useRef, useCallback } from "react"; import { loadGoogleMapScript, isBrowser } from "./utils"; -import { GOOGLE_MAP_SCRIPT_BASE_URL } from "./constants"; export default function usePlacesWidget(props) { const { @@ -21,19 +20,22 @@ export default function usePlacesWidget(props) { bounds, ...options } = {}, - googleMapsScriptBaseUrl = GOOGLE_MAP_SCRIPT_BASE_URL, language, } = props; const inputRef = useRef(null); const event = useRef(null); const autocompleteRef = useRef(null); const observerHack = useRef(null); - const languageQueryParam = language ? `&language=${language}` : ""; - const googleMapsScriptUrl = `${googleMapsScriptBaseUrl}?libraries=places&key=${apiKey}${languageQueryParam}`; const handleLoadScript = useCallback( - () => loadGoogleMapScript(googleMapsScriptBaseUrl, googleMapsScriptUrl), - [googleMapsScriptBaseUrl, googleMapsScriptUrl] + () => + loadGoogleMapScript({ + apiKey, + version: "weekly", + libraries: ["places"], + language, + }), + [] ); useEffect(() => { @@ -52,22 +54,16 @@ export default function usePlacesWidget(props) { if (ref && !ref.current) ref.current = inputRef.current; - const handleAutoComplete = () => { - if (typeof google === "undefined") - return console.error( - "Google has not been found. Make sure your provide apiKey prop." - ); - - if (!google.maps.places) + const handleAutoComplete = (service) => { + if ((apiKey && !service.maps.places) || (!apiKey && !google.maps.places)) return console.error("Google maps places API must be loaded."); if (!inputRef.current instanceof HTMLInputElement) return console.error("Input ref must be HTMLInputElement."); - autocompleteRef.current = new google.maps.places.Autocomplete( - inputRef.current, - config - ); + autocompleteRef.current = new ( + service || google + ).maps.places.Autocomplete(inputRef.current, config); event.current = autocompleteRef.current.addListener( "place_changed", @@ -84,7 +80,7 @@ export default function usePlacesWidget(props) { }; if (apiKey) { - handleLoadScript().then(() => handleAutoComplete()); + handleLoadScript().then(handleAutoComplete); } else { handleAutoComplete(); } diff --git a/src/utils.js b/src/utils.js index da11357..8e7f985 100644 --- a/src/utils.js +++ b/src/utils.js @@ -1,35 +1,11 @@ +import { Loader } from "@googlemaps/js-api-loader"; + export const isBrowser = typeof window !== "undefined" && window.document; -export const loadGoogleMapScript = ( - googleMapsScriptBaseUrl, - googleMapsScriptUrl -) => { +export const loadGoogleMapScript = (options) => { if (!isBrowser) return Promise.resolve(); - if (typeof google !== "undefined") { - if (google.maps && google.maps.api) return Promise.resolve(); - } - - const scriptElements = document.querySelectorAll( - `script[src*="${googleMapsScriptBaseUrl}"` - ); - - if (scriptElements && scriptElements.length) { - return new Promise((resolve) => { - // in case we already have a script on the page and it's loaded we resolve - if (typeof google !== "undefined") return resolve(); - - // otherwise we wait until it's loaded and resolve - scriptElements[0].addEventListener("load", () => resolve()); - }); - } - - const el = document.createElement("script"); - el.src = googleMapsScriptUrl; - - document.body.appendChild(el); + const loader = new Loader(options); - return new Promise((resolve) => { - el.addEventListener("load", () => resolve()); - }); + return loader.load(); };