Poznajemy API, które jest w stanie pokazać nam nasze położenie geograficzne. Do dzieła.

Sposób użycia:

navigator.geolocation.getCurrentPosition(success, error, options);

Argumenty:

  • success (obowiązkowe): callback, który przyjmie pozycję i wywoła się, gdy mamy sukces (po pierwsze, zezwalamy na użycie geolokacji, po drugie kod zakończył działanie)
  • error (opcjonalne): callback, który ma się wykonać, gdy wystąpi jakiś błąd
  • options (opcjonalne): obiekt, zawierający opcje:
    • enableHighAccuracy: domyślnie false, jak true to działa wolniej i więcej energii używa, ale lepsze wyniki podaje
    • timeout: ile maksymalnie ma czekać, aż zwróci wynik (w ms)
    • maximumAge: domyślnie 0, też w ms, ile czasu może mieć maksymalnie zkeszowany wynik, aby można go było uznać za akceptowalny

Oto przykład użycia z MDN:

const options = {
  enableHighAccuracy: true,
  timeout: 5000,
  maximumAge: 0,
};

function success(pos) {
  const crd = pos.coords;

  console.log("Your current position is:");
  console.log(`Latitude : ${crd.latitude}`);
  console.log(`Longitude: ${crd.longitude}`);
  console.log(`More or less ${crd.accuracy} meters.`);
}

function error(err) {
  console.warn(`ERROR(${err.code}): ${err.message}`);
}

navigator.geolocation.getCurrentPosition(success, error, options);

Dobra, a teraz custom hook w React useGeolocation:

import { useState } from "react";

function useGeolocation() {
  const [isLoading, setIsLoading] = useState(false);
  const [position, setPosition] = useState({});
  const [error, setError] = useState(null);

  function getPosition() {
    if (!navigator.geolocation)
      return setError("Your browser does not support geolocation");

    setIsLoading(true);
    navigator.geolocation.getCurrentPosition(
      (pos) => {
        setPosition({
          lat: pos.coords.latitude,
          lng: pos.coords.longitude
        });
        setIsLoading(false);
      },
      (error) => {
        setError(error.message);
        setIsLoading(false);
      }
    );
  }

  return { isLoading, position, error, getPosition };
}

Chyba proste i nie trzeba tłumaczyć. No, może ten return setError, to warto zaznaczyć – jako że return, to ustawiamy i wychodzimy, dalej kod się nie wykonuje.

Możemy jeszcze zobaczyć jak autor sobie użył tego kodu:

export default function App() {
  const {
    isLoading,
    position: { lat, lng },
    error,
    getPosition
  } = useGeolocation();

  const [countClicks, setCountClicks] = useState(0);

  function handleClick() {
    setCountClicks((count) => count + 1);
    getPosition();
  }

  return (
    <div>
      <button onClick={handleClick} disabled={isLoading}>
        Get my position
      </button>

      {isLoading && <p>Loading position...</p>}
      {error && <p>{error}</p>}
      {!isLoading && !error && lat && lng && (
        <p>
          Your GPS position:{" "}
          <a
            target="_blank"
            rel="noreferrer"
            href={`https://www.openstreetmap.org/#map=16/${lat}/${lng}`}
          >
            {lat}, {lng}
          </a>
        </p>
      )}

      <p>You requested position {countClicks} times</p>
    </div>
  );
}

Mam nadzieję, że dekompozycja obiektów nas nie dziwi. Wystarczy wywołać get postion aby wszystko działo się samo, tam w mapce mamy użycie tej dekompozycji lat i lng.

Oczywiście jeżeli nie pozwolimy na geolokację, to potem musimy odświeżyć stronę, inaczej kolejne kliknięcia niewiele zmienią. Zezwolenia to jest Permissions API, zupełnie inny temat.