0

I have an app that upon the user loading it, uses Javascript's built-in API to fetch geolocation data. Then, it passes this data along to a component that is supposed to use this in an API call to OpenWeather. However, the API call is happening well before the Geolocation can load. I have tried making my useLocation function asynchronous to await (success) but it did not work.

Here is the code to my App.js

 const useLocation = () => {
  const [location, setLocation] = useState({lat: 0, long: 0})

    useEffect(()=>{
      const success = (position) =>{ 
        let lat = position.coords.latitude
        let long = position.coords.longitude
   
        
        console.log(lat, long)
        setLocation({lat: lat, long: long})
        
      }
       navigator.geolocation.getCurrentPosition(success)

    },[])

    return location;

}


function App() {
  // Gets just the
 const location = useLocation()

function App() {
  // Gets just the
 const location = useLocation()
    
  const routes = ['nasa','openweather','zomato']

  return ( 
    <div className="App"> 

 

      <Route exact path="/openweather">
        <Weather position={location} />
      </Route>

</div>

}

Here is the code for Weather.js

import { useState, useEffect } from "react"

const Weather = ({ position }) => {

    const long = position.long
    const lati = position.lat

    const APIKey = '1234'

    const [weather, setWeather] = useState()

    const initData = async () => {
        const response = await fetch(`https://api.openweathermap.org/data/2.5/weather?lat=${lati}&lon=${long}&appid=${APIKey}`)
        
        const weatherData = await response.json()
            setWeather(weatherData)


        console.log(response)
    }

    useEffect(()=> {
        initData()
    },[])
Artem
  • 7
  • 3
  • You need to *check* if the geolocation data is loaded `if (long && lat) { initData() }`. You will probably also want a better sentinel value than zero, since zero is a perfectly reasonable latitude or longitude value. See my comment on the answer to your last question. – Jared Smith Feb 08 '21 at 16:08
  • Does this answer your question? [How do I return the response from an asynchronous call?](https://stackoverflow.com/questions/14220321/how-do-i-return-the-response-from-an-asynchronous-call) – Jared Smith Feb 08 '21 at 16:11
  • no, my issue is not returning the call, it's the order in which the function is run. Geolocation takes longer to load than the api call. – Artem Feb 08 '21 at 16:15

1 Answers1

0

The Geolocation getCurrentPosition gets the result on a function callback success which is asynchronous

const success = (position) => { 
  const lat = position.coords.latitude
  const long = position.coords.longitude

  console.log(lat, long)
}

navigator.geolocation.getCurrentPosition(success)
console.log('I will log before geolocation gets position')

In order to make it synchronous, we need to wrap them in a Promise, and place the resolve inside the success callback function.

const geolocationGetCurrentPosition = () => {
  return new Promise((resolve, reject) => {
    const success = (position) => {
      const lat = position.coords.latitude;
      const long = position.coords.longitude;

      resolve({ lat, long });
    };
    const error = (err) => {
      reject(err);
    };
    navigator.geolocation.getCurrentPosition(success, error);
  });
};

const run = async () => {
  await geolocationGetCurrentPosition()
  console.log("I'm synchronous, I will log after geolocation gets position")
}

Here's the result code, but I changed the default location state to be null, since longitude and latitude values set to zero is a valid coordinate.

const geolocationGetCurrentPosition = () => {
  return new Promise((resolve, reject) => {
    const success = (position) => {
      const lat = position.coords.latitude;
      const long = position.coords.longitude;

      resolve({ lat, long });
    };
    const error = (err) => {
      reject(err);
    };
    navigator.geolocation.getCurrentPosition(success, error);
  });
};

const useLocation = () => {
  const [location, setLocation] = useState(null);

  useEffect(() => {
    async function fetchData() {
      try {
        const { lat, long } = await geolocationGetCurrentPosition();
        setLocation({ lat, long });
      } catch (err) {
        // err
      }
    }
    fetchData();
  }, []);

  return location;
};

function App() {
  const location = useLocation();

  const routes = ["nasa", "openweather", "zomato"];

  return (
    <div className="App">
      {location && (
        <Route exact path="/openweather">
          <Weather position={location} />
        </Route>
      )}
    </div>
  );
}
Caleb Taylor
  • 2,670
  • 2
  • 21
  • 31