skip to Main Content

I need to know whether the user clicked on "Allow" or "Deny" while accessing a webpage built using React+Typescript but so far I have been unable to get it working properly. See the following piece of code:

import { useEffect, useState } from "react";
import useGeolocation from "./useGeolocation";

export default function App() {
  const [locationAccess, setLocationAccess] = useState<boolean>(false);

  useEffect(() => {
    navigator.permissions
      .query({ name: "geolocation" })
      .then((permissionStatus) => {
        setLocationAccess(permissionStatus.state === "granted");
      });
  }, [locationAccess]);

  const geoLocation = useGeolocation({ locationAccess });

  console.log("geoLocation", geoLocation);

  return <></>;
}

When I access the page for the first time and you are requested to give permission to access your geolocation I am able to see the following object being return:

{
    "loaded": false,
    "coordinates": {
        "lat": "",
        "lng": ""
    },
    "locale": "",
    "countryCode": "",
    "error": {}
}

which is fine because useGeolocation does return such an object if no access has been allowed| or denied. Now if I click on "Allow" I would expect right away to see the following object being returned:

{
    "loaded": true,
    "coordinates": {
        "lat": 28.504016,
        "lng": -82.5510363
    },
    "locale": "en-us",
    "countryCode": "US",
    "error": {}
}

but instead, I have to reload the page to get the values back. Is it possible to achieve this once the user clicks the "Allow" button?

I have set up a CodeSanbox if you need access to the code and to play with it here

2

Answers


  1. One way to address this is to move the useGeolocation hook’s logic inside the useEffect in the main component, so that it can directly listen to the changes in the locationAccess state. Here’s an updated version of your code:

    import { useEffect, useState } from "react";
    import useGeolocation from "./useGeolocation";
    
    export default function App() {
      const [locationAccess, setLocationAccess] = useState<boolean>(false);
      const [geoLocation, setGeoLocation] = useState({
        loaded: false,
        coordinates: {
          lat: "",
          lng: ""
        },
        locale: "",
        countryCode: "",
        error: {}
      });
    
      useEffect(() => {
        navigator.permissions
          .query({ name: "geolocation" })
          .then((permissionStatus) => {
            setLocationAccess(permissionStatus.state === "granted");
          });
      }, [locationAccess]);
    
      useEffect(() => {
        if (locationAccess) {
          // Call the logic of useGeolocation directly
          navigator.geolocation.getCurrentPosition(
            (position) => {
              setGeoLocation({
                loaded: true,
                coordinates: {
                  lat: position.coords.latitude,
                  lng: position.coords.longitude
                },
                locale: navigator.language,
                countryCode: navigator.language.slice(3).toUpperCase(),
                error: {}
              });
            },
            (error) => {
              setGeoLocation({
                loaded: true,
                coordinates: {
                  lat: "",
                  lng: ""
                },
                locale: "",
                countryCode: "",
                error: error.message
              });
            }
          );
        }
      }, [locationAccess]);
    
      console.log("geoLocation", geoLocation);
    
      return <></>;
    }
    
    

    In this modification, the logic to fetch the geolocation is moved into the main useEffect, which listens to changes in the locationAccess state. This should result in an immediate update of the geoLocation state when the user grants permission.

    Login or Signup to reply.
  2. When the PermissionStatus‘s state is "prompt", you’re supposed to wait for its onchange event to actually know if it has been "granted" or "denied".

    So you need to modify your code to look like

    useEffect(() => {
      navigator.permissions
        .query({ name: "geolocation" })
        .then((permissionStatus) => {
          if (permissionStatus.state === "prompt") {
            permissionStatus.onchange = (evt) => {
              setLocationAccess(permissionStatus.state === "granted");
            };
          }
          else { // User has already saved a setting
            setLocationAccess(permissionStatus.state === "granted");
          }
        });
    }, [locationAccess]);
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search