react-geolocated
Version:
React hook for using Geolocation API
117 lines (116 loc) • 4.76 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.useGeolocated = useGeolocated;
const react_1 = require("react");
/**
* Hook abstracting away the interaction with the Geolocation API.
* @param config - the configuration to use
*/
function useGeolocated(config = {}) {
const { positionOptions = {
enableHighAccuracy: true,
maximumAge: 0,
timeout: Infinity,
}, isOptimisticGeolocationEnabled = true, userDecisionTimeout = undefined, suppressLocationOnMount = false, watchPosition = false, geolocationProvider = typeof navigator !== "undefined"
? navigator.geolocation
: undefined, watchLocationPermissionChange = false, onError, onSuccess, } = config;
const userDecisionTimeoutId = (0, react_1.useRef)(0);
const isCurrentlyMounted = (0, react_1.useRef)(true);
const watchId = (0, react_1.useRef)(0);
const [isGeolocationEnabled, setIsGeolocationEnabled] = (0, react_1.useState)(isOptimisticGeolocationEnabled);
const [coords, setCoords] = (0, react_1.useState)();
const [timestamp, setTimestamp] = (0, react_1.useState)();
const [positionError, setPositionError] = (0, react_1.useState)();
const [permissionState, setPermissionState] = (0, react_1.useState)();
const cancelUserDecisionTimeout = (0, react_1.useCallback)(() => {
if (userDecisionTimeoutId.current) {
window.clearTimeout(userDecisionTimeoutId.current);
}
}, []);
const handlePositionError = (0, react_1.useCallback)((error) => {
cancelUserDecisionTimeout();
if (isCurrentlyMounted.current) {
setCoords(() => undefined);
setIsGeolocationEnabled(false);
setPositionError(error);
}
onError === null || onError === void 0 ? void 0 : onError(error);
}, [onError, cancelUserDecisionTimeout]);
const handlePositionSuccess = (0, react_1.useCallback)((position) => {
cancelUserDecisionTimeout();
if (isCurrentlyMounted.current) {
setCoords(position.coords);
setTimestamp(position.timestamp);
setIsGeolocationEnabled(true);
setPositionError(() => undefined);
}
onSuccess === null || onSuccess === void 0 ? void 0 : onSuccess(position);
}, [onSuccess, cancelUserDecisionTimeout]);
const getPosition = (0, react_1.useCallback)(() => {
if (!geolocationProvider ||
!geolocationProvider.getCurrentPosition ||
!geolocationProvider.watchPosition) {
throw new Error("The provided geolocation provider is invalid");
}
if (userDecisionTimeout) {
userDecisionTimeoutId.current = window.setTimeout(() => {
handlePositionError();
}, userDecisionTimeout);
}
if (watchPosition) {
watchId.current = geolocationProvider.watchPosition(handlePositionSuccess, handlePositionError, positionOptions);
}
else {
geolocationProvider.getCurrentPosition(handlePositionSuccess, handlePositionError, positionOptions);
}
}, [
geolocationProvider,
watchPosition,
userDecisionTimeout,
handlePositionError,
handlePositionSuccess,
positionOptions,
]);
(0, react_1.useEffect)(() => {
let permission;
if (watchLocationPermissionChange &&
geolocationProvider &&
"permissions" in navigator) {
navigator.permissions
.query({ name: "geolocation" })
.then((result) => {
permission = result;
permission.onchange = () => {
setPermissionState(permission.state);
};
})
.catch((e) => {
console.error("Error updating the permissions", e);
});
}
return () => {
if (permission) {
permission.onchange = null;
}
};
}, []); // eslint-disable-line react-hooks/exhaustive-deps
(0, react_1.useEffect)(() => {
if (!suppressLocationOnMount) {
getPosition();
}
return () => {
cancelUserDecisionTimeout();
if (watchPosition && watchId.current) {
geolocationProvider === null || geolocationProvider === void 0 ? void 0 : geolocationProvider.clearWatch(watchId.current);
}
};
}, [permissionState]); // eslint-disable-line react-hooks/exhaustive-deps
return {
getPosition,
coords,
timestamp,
isGeolocationEnabled,
isGeolocationAvailable: Boolean(geolocationProvider),
positionError,
};
}