When I use Polyline it gives me this error:
attempt to invoke interface method 'java.util.iterator java.util.list.iterator()' on a null object
At first, I thought I made something wrong or I mis-used google-map API so I tried with some hard-coded coords (as shown in my code below) but without any progress.
The code will run fine if I remove the Polyline part.
I googled hoping to find any solution but without any success also.
some info my about dev platform:
expo version 2.19.1
yarn version 1.16.0
Node version 10.15.2
Testing on a physical device with android PI installed.
import React from 'react';
import { StyleSheet, Text, View } from 'react-native';
import { Permissions, MapView } from 'expo'
// import Polyline from '#mapbox/polyline'
import { Polyline } from 'react-native-maps'
const locations = require('./locations.json')
export default class App extends React.Component {
state = {
latitude: null,
longitude: null,
locations: locations
}
componentDidMount() {
this.GetLocations()
}
GetLocations = async () => {
const { status } = await Permissions.getAsync(Permissions.LOCATION)
if (status !== 'granted') {
const response = await Permissions.askAsync(Permissions.LOCATION)
}
navigator.geolocation.getCurrentPosition(
({ coords: { latitude, longitude }}) => this.setState({ latitude, longitude}, this.mergeCoords),
(err) => console.log(`Error: ${err}`)
)
const { locations: [sampleLocation] } = this.state
this.setState({
desLatitude: sampleLocation.coords.latitude,
desLongitude: sampleLocation.coords.longitude,
}, this.mergeCoords)
}
mergeCoords = () => {
const { latitude, longitude, desLatitude, desLongitude } = this.state
const hasStartAndEnd = ( latitude !== null && desLatitude !== null )
// if the line have start and end
if (hasStartAndEnd) {
const concatStart = `${latitude},${longitude}`
const concatEnd = `${desLatitude},${desLongitude}`
this.getDirections(concatStart, concatEnd)
}
}
async getDirections(startLoc, desLoc) {
try {
// const res = await fetch(`https://maps.googleapis.com/maps/api/directions/json?key=MY_API_KEY&origin=${startLoc}&destination=${desLoc}`)
// const resJson = await res.json()
// const points = Polyline.decode(resJson.routes[0].overview_polyline.points)
// const coords = points.map( point => {
// return {
// latitude: point[0],
// longitude: point[1]
// }
// })
const coords = {
latitude: 31.262353,
longitude: 29.989506,
}
console.log("point, coords: ", coord)
this.setState({coords})
} catch(err) {
console.log('Error: ', err)
}
}
render() {
const { latitude, longitude, coords } = this.state
if (latitude) {
return (
<MapView
style={{ flex: 1 }}
showsUserLocation
initialRegion={{
latitude,
longitude,
latitudeDelta: 0.0922,
longitudeDelta: 0.0421,
}}
>
<MapView.Polyline
strokeWidth={2}
strokeColor="red"
coordinates={coords}
/>
</MapView>
)
}
return (
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center'}}>
<Text>We need your permissions !!</Text>
</View>
)
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
alignItems: 'center',
justifyContent: 'center',
},
});
//use condional rendering
import React from 'react';
import { StyleSheet, Text, View } from 'react-native';
import { Permissions, MapView } from 'expo'
// import Polyline from '#mapbox/polyline'
import { Polyline } from 'react-native-maps'
const locations = require('./locations.json')
export default class App extends React.Component {
state = {
latitude: null,
longitude: null,
locations: locations,
direct : false
}
componentDidMount() {
this.GetLocations()
}
GetLocations = async () => {
const { status } = await Permissions.getAsync(Permissions.LOCATION)
if (status !== 'granted') {
const response = await Permissions.askAsync(Permissions.LOCATION)
}
navigator.geolocation.getCurrentPosition(
({ coords: { latitude, longitude }}) => this.setState({ latitude, longitude},
this.mergeCoords),
(err) => console.log(`Error: ${err}`)
)
const { locations: [sampleLocation] } = this.state
this.setState({
desLatitude: sampleLocation.coords.latitude,
desLongitude: sampleLocation.coords.longitude,
}, this.mergeCoords)
}
mergeCoords = () => {
const { latitude, longitude, desLatitude, desLongitude } = this.state
const hasStartAndEnd = ( latitude !== null && desLatitude !== null )
// if the line have start and end
if (hasStartAndEnd) {
const concatStart = `${latitude},${longitude}`
const concatEnd = `${desLatitude},${desLongitude}`
this.getDirections(concatStart, concatEnd)
}
}
async getDirections(startLoc, desLoc) {
try {
// const res = await fetch(`https://maps.googleapis.com/maps/api/directions/json?
key=MY_API_KEY&origin=${startLoc}&destination=${desLoc}`)
// const resJson = await res.json()
// const points = Polyline.decode(resJson.routes[0].overview_polyline.points)
// const coords = points.map( point => {
// return {
// latitude: point[0],
// longitude: point[1]
// }
// })
const coords = {
latitude: 31.262353,
longitude: 29.989506,
}
console.log("point, coords: ", coord)
this.setState({coords, direct:true})
} catch(err) {
console.log('Error: ', err)
}
}
render() {
const { latitude, longitude, coords } = this.state
if (latitude) {
return (
<MapView
style={{ flex: 1 }}
showsUserLocation
initialRegion={{
latitude,
longitude,
latitudeDelta: 0.0922,
longitudeDelta: 0.0421,
}}
>
{this.state.direct &&
<MapView.Polyline
strokeWidth={2}
strokeColor="red"
coordinates={coords}
/>}
</MapView>
)
}
return (
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center'}}>
<Text>We need your permissions !!</Text>
</View>
)
}
}
Related
I want to switch back and forth between two React components as shown below.
Every time the Photo Component is invoked and the user has taken a picture. I want the Game component to mount again (thus passing condition counter state variable as props, to be changed) before the photo component unmounts.
React is yelling at me for Warning: Can't perform a React state update on an unmounted component.
I understand the problem that my Photo component unmounts first before the state has updated.
I have looked into several similar questions and try to adopt their solutions e.g. having an additional variable to check if the component is mounted or not and few others.
Seems like none helped me.
Game.js
import React, { useState, useEffect } from 'react';
import { TextInput, Platform, Text, View, StyleSheet, Button, Alert } from 'react-native';
import Constants from 'expo-constants';
import * as Location from 'expo-location';
import * as Permissions from 'expo-permissions';
import MapView, { Marker, Circle, Polyline } from 'react-native-maps';
import Header from '../Components/Header';
import Footer from '../Components/Footer';
import PhotoHandler from './Photo';
import * as geolib from 'geolib';
import { event } from 'react-native-reanimated';
export default function GameArea() {
const [isFetching, setIsFetching] = useState(false);
const [generateTargets, setGenerateTargets] = useState(true);
const [targets, setTargets] = useState([]);
const [distances, setDistances] = useState([]);
const [index, setIndex] = useState(null);
const [gesture, setGesture] = useState({});
const [draggingMap, setdraggingMap] = useState(false);
const [numofTargets, setNumofTargets] = useState(null);
const [conditionCounter, setConditionCounter] = useState(false);
const [photoTaken, setPhotoTaken] = useState(false);
const [pickedLocation, setPickedLocation] = useState({
latitude: 123,
longitude: 123
});
const [errorMsg, setErrorMsg] = useState(null);
const [region, setRegion] = useState({
latitude: 123,
longitude: 123,
latitudeDelta: 0.001,
longitudeDelta: 0.001
});
const [mapBounds, setMapBounds] = useState({
n: null,
s: null,
e: null,
w: null
});
const [mapRef, updateMapRef] = useState(null);
useEffect(() => {
const verifyPermissions = async () => {
const result = await Permissions.askAsync(Permissions.LOCATION);
if (result.status !== 'granted') {
Alert.alert(
'Insufficient permissions!',
'You need to grant location permissions to use this app.',
[{ text: 'Okay' }]
);
return false;
}
return true;
};
(async () => {
const hasPermission = await verifyPermissions();
if (!hasPermission) {
return;
}
try {
setIsFetching(true);
const location = await Location.getCurrentPositionAsync({
});
setPickedLocation({
latitude: location.coords.latitude,
longitude: location.coords.longitude
});
setRegion({
latitude: location.coords.latitude,
longitude: location.coords.longitude,
latitudeDelta: 0.003,
longitudeDelta: 0.003
});
} catch (err) {
Alert.alert(
'Could not fetch location!',
'Please try again.',
[{ text: 'Okay' }]
);
}
setIsFetching(false);
})();
}, []);
const getBoundaries = () => {
if (mapRef === null) {
return;
}
mapRef
.getMapBoundaries()
.then((res) => {
setMapBounds({
n: res.northEast.latitude,
e: res.northEast.longitude,
s: res.southWest.latitude,
w: res.southWest.longitude
});
})
.catch((err) => console.log(err));
};
const onRegionChangeComplete = (region) => {
if (draggingMap === true && targets.length == 0) {
getBoundaries();
setRegion({
latitude: region.latitude,
longitude: region.longitude,
latitudeDelta: region.latitudeDelta,
longitudeDelta: region.longitudeDelta
})
setdraggingMap(false);
}
else {
return;
}
}
function handleChange(newValue) {
setConditionCounter(newValue);
}
const ondraggingMap = () => {
setdraggingMap(true);
}
async function generatedTargets() {
var url = `https://xxx.yy.zz`;
if (numofTargets == null) {
Alert.alert(
"Missing",
"Enter Number of targets first",
[
{ text: "OK" }
]
);
}
else {
try {
let response = await fetch(
url,
);
let responseJson = await response.json();
console.log("Targets generated");
console.log(responseJson);
setTargets(responseJson);
setGenerateTargets(false);
} catch (error) {
Alert.alert(
"Failed: Network Error",
"Try again",
[
{ text: "OK" }
]
);
console.error(error);
}
}
}
useEffect(() => {
console.log('inside useEffect hook for measuring targets distances');
var lat1 = pickedLocation.latitude;
var lng1 = pickedLocation.longitude;
console.log("targets distance");
const R = 6371;
let targets_distance = [];
for (i = 0; i < targets.length; i++) {
const φ1 = lat1 * Math.PI / 180;
const φ2 = targets[i].lat * Math.PI / 180;
const Δφ = (targets[i].lat - lat1) * Math.PI / 180;
const Δλ = (targets[i].lng - lng1) * Math.PI / 180;
const a = Math.sin(Δφ / 2) * Math.sin(Δφ / 2) +
Math.cos(φ1) * Math.cos(φ2) *
Math.sin(Δλ / 2) * Math.sin(Δλ / 2);
const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
const distance = R * c * 1000;
console.log('distance is'+ distance);
if(distance <=20){
setConditionCounter(true); //switch to photo component
targets.splice(i, 1);
console.log("targets after removal");
console.log(targets);
}
else{
targets_distance.push(distance);
}
}
console.log(targets_distance);
var index = 0;
var value = targets_distance[0];
for (var i = 1; i < targets_distance.length; i++) {
if (targets_distance[i] < value) {
value = targets_distance[i];
index = i;
}
}
console.log("smallest element is " + value + " at index " + index);
setIndex(index);
setDistances(targets_distance);
}, [pickedLocation]);
useEffect(() => {
console.log("Inside UseEffect watch position");
(async () => {
try {
await Location.watchPositionAsync(
{
accuracy: Location.Accuracy.BestForNavigation,
distanceInterval: 10,
timeInterval: 3000,
},
(loc) => {
setPickedLocation({
latitude: loc.coords.latitude,
longitude: loc.coords.longitude
});
}
);
} catch (e) {
Alert.alert("Error");
}
})();
}, []);
const setNumberOfTargets = (numofTargets) =>{
setNumofTargets(numofTargets);
}
return (
<View style={styles.container}>
<Header title="Crowdsourcing" />
{conditionCounter?
<PhotoHandler conditionCounter={setConditionCounter} onChange={handleChange}/>:
<MapView style={styles.mapContainer}
region={{
latitude: region.latitude,
longitude: region.longitude,
latitudeDelta: region.latitudeDelta,
longitudeDelta: region.longitudeDelta
}}
showsUserLocation={true}
followUserLocation={true}
ref={(ref) => updateMapRef(ref)}
onPanDrag={ondraggingMap}
onRegionChangeComplete={onRegionChangeComplete}
>
{
targets.map((target, index) => (
<MapView.Marker
key={index}
coordinate={{
latitude: target.lat,
longitude: target.lng
}}
title={target.name}
/>
))}
</MapView>}
<Footer
Targets = {generateTargets}
numberOfTargets = {numofTargets}
setNumTargets = {setNumberOfTargets}
osmTargets = {generatedTargets}
/>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
alignItems: 'center',
justifyContent: 'center',
flexDirection: 'column',
width: '100%',
height: '100%'
},
mapContainer: {
width: '100%',
height: 500,
marginBottom: 20
},
footer: {
width: '100%',
height: 70,
backgroundColor: '#FF7F50',
alignItems: 'center',
justifyContent: 'center'
},
buttonContainer: {
flexDirection: 'row',
width: '100%',
justifyContent: 'space-evenly'
}
});
Photo.js
import React, { useState, useEffect } from 'react';
import { StyleSheet, Text, View, Alert, Button, Image } from 'react-native';
import * as ImagePicker from 'expo-image-picker';
import * as Permissions from 'expo-permissions';
import * as Location from 'expo-location';
export default function PhotoHandler(props) {
const [pickedLocation, setPickedLocation] = useState({
latitude: null,
longitude: null
});
const [pickedImage, setPickedImage] = useState(null);
const verifyPermissions = async () =>{
const result = await Permissions.askAsync(Permissions.CAMERA_ROLL);
if(result.status != 'granted'){
Alert.alert(
'Insufficient Permissions!',
'You need to grant camera permissions to use this app',
[{text: 'Okay'}]
);
return false;
}
return true;
};
const imagetakenHandler = async () =>{
const hasPermission = await verifyPermissions();
if(!hasPermission){
return;
}
const image = await ImagePicker.launchCameraAsync({
mediaTypes: ImagePicker.MediaTypeOptions.Images,
quality: 1,
aspect: [1, 1],
base64: true,
exif: true
});
setPickedImage(image.base64);
};
useEffect(() => {
const requestOptions = {
method: 'POST',
headers: 'Content-type: application/x-www-form-urlencoded',
};
if(pickedImage != null){
props.onChange(false); // to switch back to Game component
let response = fetch(`https://xxx.yy.zz`, requestOptions)
.then(response => response.json())
.then(console.log('SUCCESS: '+response))
.catch(e => {
console.log(e);
});
}
}, [pickedImage])
return (
<View style={styles.container}>
{
Alert.alert(
"Success",
"Take photo",
[
{text: "Open Camera",
onPress: imagetakenHandler
}
]
)
}
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
marginTop: 30
}
});
I have a question about react-native MapViewDirection with the expo.
I have my result received from the onReady function inside MapViewDirection.
I want to display the distance and duration field to the user.
onReady={result => {
console.log(result)
}};
I want to declare I am learning and am using someone else's code to practice. Below is full code:
import React, { Component } from 'react';
import { Dimensions, StyleSheet } from 'react-native';
import MapView from 'react-native-maps';
import MapViewDirections from 'react-native-maps-directions';
const { width, height } = Dimensions.get('window');
const ASPECT_RATIO = width / height;
const LATITUDE = 40.3788;
const LONGITUDE = -105.5143;
const LATITUDE_DELTA = 0.0192;
const LONGITUDE_DELTA = LATITUDE_DELTA * ASPECT_RATIO;
const GOOGLE_MAPS_APIKEY = 'AIzaSyAkwAlX2w0S3ba6OxBqr13JGDuYIi5GRZ8';
class Example extends Component {
constructor(props) {
super(props);
this.state = {
coordinates: [
{
latitude:40.3788,
longitude:-105.5143,
},
],
};
this.mapView = null;
}
onMapPress = (e) => {
this.setState({
coordinates: [
...this.state.coordinates,
e.nativeEvent.coordinate,
],
});
}
render() {
return (
<MapView
initialRegion={{
latitude: LATITUDE,
longitude: LONGITUDE,
latitudeDelta: LATITUDE_DELTA,
longitudeDelta: LONGITUDE_DELTA,
}}
style={StyleSheet.absoluteFill}
ref={c => this.mapView = c}
onPress={this.onMapPress}
>
{this.state.coordinates.map((coordinate, index) =>
<MapView.Marker key={`coordinate_${index}`} coordinate={coordinate} />
)}
{(this.state.coordinates.length >= 2) && (
<MapViewDirections
origin={this.state.coordinates[0]}
waypoints={ (this.state.coordinates.length > 2) ? this.state.coordinates.slice(1, -1): null}
destination={this.state.coordinates[this.state.coordinates.length-1]}
apikey={GOOGLE_MAPS_APIKEY}
strokeWidth={3}
strokeColor="hotpink"
optimizeWaypoints={true}
onStart={(params) => {
console.log(`Started routing between "${params.origin}" and "${params.destination}"`);
}}
onReady={result => {
console.log(result);
this.mapView.fitToCoordinates(result.coordinates, {
edgePadding: {
right: (width / 20),
bottom: (height / 20),
left: (width / 20),
top: (height / 20),
}
});
}}
onError={(errorMessage) => {
// console.log('GOT AN ERROR');
}}
/>
)}
</MapView>
);
}
}
export default Example;
Using the below given code you can get the distance between two locations using there latitude & longitude. We are using google distance matrix api here to get distance.
getDistanceOneToOne(lat1, lng1, lat2, lng2)
{
const Location1Str = lat1 + "," + lng1;
const Location2Str = lat2 + "," + lng2;
let GOOGLE_API_KEY = "API_KEY"
let ApiURL = "https://maps.googleapis.com/maps/api/distancematrix/json?"
let params = `origins=${Location1Str}&destinations=${Location2Str}&key=${GOOGLE_API_KEY}`; // you need to get a key
let finalApiURL = `${ApiURL}${encodeURI(params)}`;
this.getDistance(finalApiURL).then((getDistance) => {
console.log('Distance charges',getDistance);
if (getDistance.status == "OK")
{
let value = getDistance.rows[0].elements[0].distance.text; //Distance
let time = getDistance.rows[0].elements[0].duration.value; //time
}
});
}
getDistance = (url) => {
return this.getDistanceCall(url)
}
getDistanceCall = (url) => {
return fetch(url, {
method: 'GET',
})
.then((response) => { return response.json(); })
.catch((error) => {
return error;
});
}
When I navigate my app in startTabBasedApp (My 3 tabs) 1st screen to Render is the EventMap.js this is where my App crash. I just do not know why it crashing I tried to put console.log in all part of my codes but no error appeared.
So the main problem here is in the EventMap.js, because when I tried to remove the EventMap.js in my startTabBasedApp (Main Tabs) then uninstall the app, run react-native run-android, open the App navigate the Tabs (2) it works fine.
What I'm trying to do in my App is when user open the it and navigate it to the EventMap.js I want to get user's location instantly, just like in the Grab App.
How can I achieve this without crashing the App?
Here are my codes for EventMap.js
class EventMap extends Component {
constructor(props) {
super(props);
this.state = {
focusedLocation: {
latitude: 0,
longitude: 0,
latitudeDelta: 0.01,
longitudeDelta: Dimensions.get('window').width / Dimensions.get('window').height * 0.01
},
locationChosen: false,
markerPosition: {
latitude: 0,
longitude: 0
}
}
}
componentDidMount() {
this.didMountGetUserLocation();
};
//This is getting the location and exactly/automatically when they open
didMountGetUserLocation = () => {
navigator.geolocation.getCurrentPosition(pos => {
var lat = parseFloat(pos.coords.latitude)
var long = parseFloat(pos.coords.longitude)
var initialRegion = {
latitude: lat,
longitude: long,
latitudeDelta: 0.01,
longitudeDelta: Dimensions.get('window').width / Dimensions.get('window').height *0.01
};
this.setState({focusedLocation: initialRegion})
this.setState({locationChosen: true})
this.setState({markerPosition: initialRegion})
},
err => {
console.log(err);
});
};
pickLocationHandler = (event) => {
const coords = event.nativeEvent.coordinate;
//For animation of map
this.map.animateToRegion({
...this.state.focusedLocation,
latitude: coords.latitude,
longitude: coords.longitude
});
this.setState(prevState => {
return {
focusedLocation: {
...prevState.focusedLocation,
latitude: coords.latitude,
longitude: coords.longitude
},
locationChosen: true
};
});
};
getLocationHandler = () => {
navigator.geolocation.getCurrentPosition(pos => {
const coordsEvent = {
nativeEvent: {
coordinate: {
latitude: pos.coords.latitude,
longitude: pos.coords.longitude
}
}
};
this.pickLocationHandler(coordsEvent);
},
err => {
console.log(err);
alert("Fetching failed");
})
};
render() {
let marker = null;
if(this.state.locationChosen) {
marker = <MapView.Marker coordinate={this.state.markerPosition}/>
}
return(
<View style={{zIndex: -1}}>
<TouchableOpacity onPress={this.getLocationHandler} style={styles.iconContainer}>
<Icon name="md-locate" size={30} color="blue"/>
</TouchableOpacity>
<MapView
style={styles.map}
initialRegion={this.state.focusedLocation}
onPress={this.pickLocationHandler}
showsUserLocation={true}
ref={ref => this.map = ref} //For animating map movement
>
{marker}
</MapView>
</View>
);
}
}
I have a screen that loads and places markers on a map based on a search form in the previous screen. I want the map window to also centre itself in the middle of all the markers. So when I use initialRegion, I set the latitude and longitude to state values whose states are set after fetching JSON from a URL. The lat and long are set to values at the centre of the markers. I want the map window to go to these coordinates, but instead, I get an error when the screen loads.
Here is the code:
import React, { Component } from 'react';
import { View, Text, AsyncStorage, Alert, FlatList, StyleSheet } from 'react-native';
import { PrimaryButton } from '../Buttons';
import styles from './styles';
import { ListItem } from '../ListItem';
import MapView, { Marker } from 'react-native-maps';
class RestOptions extends Component {
constructor() {
super();
this.state = {
jsonResults: [],
userPlaces: [],
lat_center: null,
lng_center: null
}
}
renderItem = ({ item }) => {
return (
<View>
<Text>{item.rest_name}</Text>
<Text>{item.counter}</Text>
<Text>Distance: {item.distance} Miles</Text>
<PrimaryButton
label="Set Reservation"
onPress={() => this.setReservation(item.rest_id)}
/>
</View>
)
}
componentDidMount() {
this.getSearchResults();
}
getSearchResults() {
fetch('fetch url here')
.then((response) => response.json())
.then((responseJson) => {
var placesArray = [];
var latArray = [];
var lngArray = [];
for (key = 0; key < responseJson.rest_array.length; key = key + 1) {
var lati_str = responseJson.rest_array[key].lat;
var long_str = responseJson.rest_array[key].lng;
var count_str = responseJson.rest_array[key].counter;
var lati = parseFloat(lati_str);
var long = parseFloat(long_str);
var count = parseFloat(count_str);
latArray.push(lati);
lngArray.push(long);
placesArray.push ({
coordinates: {
latitude: lati,
longitude: long
},
id: count
});
}
var max_lat = Math.max.apply(null, latArray);
var min_lat = Math.min.apply(null, latArray);
var max_lng = Math.max.apply(null, lngArray);
var min_lng = Math.min.apply(null, lngArray);
var latCenter = (max_lat + min_lat) / 2;
var lngCenter = (max_lng + min_lng) / 2;
this.setState({lat_center: latCenter}); //setting latitude state here
this.setState({lng_center: lngCenter}); //setting longitude state here
this.setState({userPlaces: placesArray});
this.setState({jsonResults: responseJson.rest_array});
}).catch((error) => {
console.error(error);
});
}
setReservation(rest_id) {
Alert.alert(rest_id);
//this.props.navigation.navigate('SetReservation');
}
render() {
return (
<View>
<View style={mapStyles.mapContainer}>
<MapView
style={mapStyles.map}
initialRegion={{
latitude: this.state.lat_center, //using latitude state here
longitude: this.state.lng_center, //using longitude state here
latitudeDelta: 0.1022,
longitudeDelta: 0.0821
}}
>
{this.state.userPlaces.map(userPlace => (
<MapView.Marker
coordinate={userPlace.coordinates}
key={userPlace.id}
/>
))}
</MapView>
</View>
<FlatList
data={this.state.jsonResults}
renderItem={this.renderItem}
keyExtractor={(item, index) => index.toString()}
/>
</View>
);
}
};
const mapStyles = StyleSheet.create({
mapContainer: {
width: '100%',
height: 200,
},
map: {
width: '100%',
height: '100%',
},
});
export default RestOptions;
I get this error:
And this warning:
I have already verified that the lat_center and lng_center successfully change state to the appropriate coordinates.
It’s probably because your initial values for the lat_center and lng_center are null in your state object in the constructor.
compondentDidMount gets called after the initial render.
https://reactjs.org/docs/react-component.html#mounting
constructor()
static getDerivedStateFromProps()
render()
componentDidMount()
This means that for a moment in time the values of your latitude and longitude will be null leading to these errors. You either need to set an initial value that is not null or not render your map until the coordinates have need set.
Also those 4 setState calls could be reduced to one, in something like this.
this.setState({
lat_center: latCenter,
lng_center: lngCenter,
userPlaces: placesArray,
jsonResults: responseJson.rest_array
});
I have read the other questions related to this and tried implementing what the answers were on there.
They recommended adding /*global google */ - did not work
Next was to add const google = window.google; - did not work
I will post the code below and the error.
The error is happening where new google.maps is being called
Map.js:
/* global google */
import { default as React, Component } from 'react';
import raf from 'raf';
import canUseDOM from 'can-use-dom';
const google = window.google;
import { withGoogleMap, GoogleMap, Circle, InfoWindow, Marker } from 'react-google-maps';
import withScriptjs from 'react-google-maps/lib/async/withScriptjs';
const googleMapURL =
'https://maps.googleapis.com/maps/api/js?v=3.27&libraries=places,geometry&key=AIzaSyA7XEFRxE4Lm28tAh44M_568fCLOP_On3k';
const geolocation =
canUseDOM && navigator.geolocation
? navigator.geolocation
: {
getCurrentPosition(success, failure) {
failure("Your browser doesn't support geolocation.");
},
};
const GeolocationExampleGoogleMap = withScriptjs(
withGoogleMap(props =>
<GoogleMap defaultZoom={8} center={props.center}>
{props.center &&
<InfoWindow position={props.center}>
<div>User's Location</div>
</InfoWindow>}
{props.center &&
<Circle
center={props.center}
radius={props.radius}
options={{
fillColor: 'red',
fillOpacity: 0.2,
strokeColor: 'red',
strokeOpacity: 1,
strokeWeight: 1,
}}
/>}
>
{props.markers.map((marker, index) => {
const onClick = () => props.onMarkerClick(marker);
const onCloseClick = () => props.onCloseClick(marker);
return (
<Marker
key={index}
position={marker.position}
title={(index + 1).toString()}
onClick={onClick}
>
{marker.showInfo &&
<InfoWindow onCloseClick={onCloseClick}>
<div>
<strong>
{marker.content}
</strong>
<br />
<em>The contents of this InfoWindow are actually ReactElements.</em>
</div>
</InfoWindow>}
</Marker>
);
})}
</GoogleMap>,
),
);
function generateInitialMarkers() {
const southWest = new google.maps.LatLng(-31.203405, 125.244141);
const northEast = new google.maps.LatLng(-25.363882, 131.044922);
const lngSpan = northEast.lng() - southWest.lng();
const latSpan = northEast.lat() - southWest.lat();
const markers = [];
for (let i = 0; i < 5; i++) {
const position = new google.maps.LatLng(
southWest.lat() + latSpan * Math.random(),
southWest.lng() + lngSpan * Math.random(),
);
markers.push({
position,
content: 'This is the secret message'.split(' ')[i],
showInfo: false,
});
}
return markers;
}
export default class GeolocationExample extends Component {
constructor(props) {
super(props);
super(props);
this.state = {
center: null,
content: null,
radius: 6000,
markers: generateInitialMarkers(),
};
const isUnmounted = false;
handleMarkerClick = this.handleMarkerClick.bind(this);
handleCloseClick = this.handleCloseClick.bind(this);
}
handleMarkerClick(targetMarker) {
this.setState({
markers: this.state.markers.map((marker) => {
if (marker === targetMarker) {
return {
...marker,
showInfo: true,
};
}
return marker;
}),
});
}
handleCloseClick(targetMarker) {
this.setState({
markers: this.state.markers.map((marker) => {
if (marker === targetMarker) {
return {
...marker,
showInfo: false,
};
}
return marker;
}),
});
}
componentDidMount() {
const tick = () => {
if (this.isUnmounted) {
return;
}
this.setState({ radius: Math.max(this.state.radius - 20, 0) });
if (this.state.radius > 200) {
raf(tick);
}
};
geolocation.getCurrentPosition(
(position) => {
if (this.isUnmounted) {
return;
}
this.setState({
center: {
lat: position.coords.latitude,
lng: position.coords.longitude,
},
content: 'Location found using HTML5.',
});
raf(tick);
},
(reason) => {
if (this.isUnmounted) {
return;
}
this.setState({
center: {
lat: 60,
lng: 105,
},
content: `Error: The Geolocation service failed (${reason}).`,
});
},
);
}
componentWillUnmount() {
this.isUnmounted = true;
}
render() {
return (
<GeolocationExampleGoogleMap
googleMapURL={googleMapURL}
loadingElement={<div style={{ height: '100%' }}>loading...</div>}
containerElement={<div style={{ height: '100%' }} />}
mapElement={<div style={{ height: '100%' }} />}
center={this.state.center}
content={this.state.content}
radius={this.state.radius}
onMarkerClick={this.handleMarkerClick}
onCloseClick={this.handleCloseClick}
markers={this.state.markers}
/>
);
}
}
You are loading async script. When you initialize google constant there is no google object. You can solve this by using 'ref' for map component. Ref callback fired when google object exists. For example:
<GoogleMap defaultZoom={8} center={props.center} ref={()=>{props.onMapLoad}}>...</GoogleMap>
...
constructor(props) {
...
this.onMapLoad = this.onMapLoad.bind(this)
}
...
onMapLoad() {
/*init markers and all objects with "google" here*/
}
...
<GeolocationExampleGoogleMap
onMapLoad={this.onMapLoad}
...
/>
and, of course, do not init google const, use just google object instead,