Reputation: 103
I'm using the navigator.geolocation
API in React Native to set the user coordinates as part of the component state, which the MapView
is supposed to use as the region
prop, and running getCurrentPosition()
throws an error.
I'm not sure how to fix this. The error says this is an immutability issue, although I made sure I used let
and const
where they're supposed to be.
This is my initial state:
this.state = {
data: {
name: "Nie powinieneś tego widzieć.",
address: "Wyślij zrzut ekranu tego widoku do nas na stronie dokosciola.pl w zakładce Kontakt.",
url: "https://dokosciola.pl",
coords: {
latitude: undefined,
longitude: undefined,
latitudeDelta: 0.00922 * 1.5,
longitudeDelta: 0.00421 * 1.5
},
hours: ["8:00", "10:00", "12:00"]
}
};
This is how I use the geolocating API:
locateUser = () => {
navigator.geolocation.getCurrentPosition(
position => {
let state = this.state;
state.data.coords.latitude = position.coords.latitude;
state.data.coords.longitude = position.coords.longitude;
this.setState(state);
console.log(this.state);
},
error => Alert.alert("Ups!", `Wystąpił wewnętrzny błąd:\n${error}`),
{ enableHighAccuracy: true, timeout: 30000, maximumAge: 5000 }
);
};
I run the locateUser()
function before mounting the app, so:
componentWillMount() {
this.locateUser();
}
This is how I use the MapView
component from react-native-maps
:
<View style={{ flex: 1 }}>
<MapView
style={{ flex: 1 }}
initialRegion={this.state.data.coords}
region={this.state.data.coords}
mapType={"mutedStandard"}
>
{data.map(element => {
return (
<ChurchMarker
coords={element.coords}
key={element.id}
onPress={() => this.update(element)}
/>
);
})}
</MapView>
</View>
ChurchMarker
is a precooked Marker
, also from react-native-maps
, and data
- a simple array of objects, mocking a potential API response:
[
{
id: string,
name: string,
address: string,
url: string,
coords: {
latitude: number,
longitude: number
},
hours: string[]
},
...
]
I expect the MapView
to focus on the user coordinates as the app mounts, but the error I specified in locateUser()
executes, with the following message:
Error: you attempted to set the key `latitude` with the value `37.375834` on an object that is meant to be immutable and has been frozen.
After that there's also a warning:
Warning: Failed prop type: The prop `region.latitude` is marked as required in `MapView`, but its value is `undefined`.
and the same for the longtitude. This means the state didn't update. Any fixes to this? What am I implementing wrong?
Upvotes: 0
Views: 1270
Reputation: 2566
Your issue is clearly described in the error message: you attempted to set a key on an object that is meant to be immutable.
When you write let state = this.state
, you are creating a reference to this.state
, which is immutable.
So when you try to assign a new value to state.data.coords.latitude
you are mutating the state object, which is illegal.
Instead you should refactor your code in two steps:
data
layer):this.state = {
name: "Nie powinieneś tego widzieć.",
address: "Wyślij zrzut ekranu tego widoku do nas na stronie dokosciola.pl w zakładce Kontakt.",
url: "https://dokosciola.pl",
coords: {
latitude: undefined,
longitude: undefined,
latitudeDelta: 0.00922 * 1.5,
longitudeDelta: 0.00421 * 1.5
},
hours: ["8:00", "10:00", "12:00"]
}
newCoords
object and set it with the correct values, then overwrite state.coords
by using setState
:locateUser = () => {
navigator.geolocation.getCurrentPosition(
position => {
const newCoords = {}
newCoords.latitude = position.coords.latitude
newCoords.longitude = position.coords.longitude
this.setState({ coords: newCoords })
},
error => Alert.alert("Ups!", `Wystąpił wewnętrzny błąd:\n${error}`),
{ enableHighAccuracy: true, timeout: 30000, maximumAge: 5000 }
);
};
Upvotes: 0
Reputation: 3335
You must always update Your state
with the object way. In Your case You can try this:
locateUser = () => {
navigator.geolocation.getCurrentPosition(
position => {
this.setState({
data.coords.latitude: position.coords.latitude,
data.coords.longitude: position.coords.longitude
});
console.log(this.state)
},
error => Alert.alert("Ups!", `Wystąpił wewnętrzny błąd:\n${error}`),
{ enableHighAccuracy: true, timeout: 30000, maximumAge: 5000 }
);
};
Or if for the new state
You want to manipulate the current state
You must use the functional way of this.setState
For more information read this part of docs.
Good Luck :)
Upvotes: 1