Reputation: 69
I'm trying to load a user's current city onto the page after a call to the Geolocation API. However, the main component where the geolocation function is being called is setting state as undefined before the Promise of location returns.
My goal is to set the state with the user's current location. However, when I'm calling geolocate() in my React component, state.city is being set as undefined because it does not wait for the geolocation function to return anything.
My geolocation functions:
const geolocate = async () => {
if (!navigator.geolocation) {
alert('Geolocation is not supported by your browser. Please search for a location to look for weather');
} else {
getCoordinates().then((position) => {
getCityByCoords(position)
.then((response) => {
return response;
})
.catch(err => console.log(err));
})
.catch(err => console.log(err));
}
}
const getCoordinates = () => {
return new Promise((resolve, reject) => {
navigator.geolocation.getCurrentPosition(
position => {
return resolve(position);
},
error => reject(error)
)}
);
};
// a call to my own API to make a request to Google's geocoding API
// response from this is coming back successfully
const getCityByCoords = (position) => {
const { latitude, longitude } = position.coords;
return axios.get('/city', {
params: {
lat: latitude,
lng: longitude
}
})
}
The function call in my component:
componentDidMount() {
this.getLocation();
}
getLocation() {
geolocate()
.then(
(response) => {
this.setState({
city: response,
isLoaded: true
})
},
(error) => {
this.setState({
isLoaded: true,
error
});
})
.then(() => {
this.setState({ isLoaded: 'false' });
})
}
I think all these asynchronous calls are throwing me off.
Upvotes: 0
Views: 53
Reputation: 2681
First of all, your geolocate
function is missing return statements, so you can get the location result in the function call in your component.
Additionally, don't nest Promises, if you can return them. In the following snippet, I've rewritten your geolocate
function:
const geolocate = async () => {
if (!navigator.geolocation) {
// better would be to throw an error here, so you can handle them in your component (or wherever else)
alert(
"Geolocation is not supported by your browser. Please search for a location to look for weather"
);
} else {
// return was missing
return getCoordinates()
.then(position => getCityByCoords(position))
.catch(err => console.log(err));
}
};
After that, let's tackle your problem: You didn't initialize your state in the constructor, so on the first render, it doesn't know anything about city
or isLoaded
.
If you put the initialization of your state into your constructor, it should print your desired city, or a simple 'Waiting' paragraph:
class Geo extends React.Component {
constructor(props) {
super(props);
this.state = {
city: "",
isLoaded: false
};
}
componentDidMount() {
this.getLocation();
}
getLocation() {
geolocate()
.then(
response => {
this.setState({
city: response,
isLoaded: true
});
},
error => {
this.setState({
isLoaded: true,
error
});
}
)
.then(() => {
this.setState({ isLoaded: "false" });
});
}
render() {
if (!this.state.isLoaded) {
return <div>Waiting for Geolocation...</div>;
}
return <div>Geolocation: {this.state.city}</div>;
}
}
Upvotes: 1