an app I am trying to make involves a portion where nearby markers are fetched from my server.
I am sure that my location hook function works, however, it is not finishing before my API call is made to get markers near that location.
This has confused me for hours, so any help would be greatly appreciated!
import React, { useEffect, useState } from "react";
import MapView, { PROVIDER_GOOGLE, Marker } from "react-native-maps";
import { StyleSheet, View, Dimensions, Platform } from "react-native";
import useLocation from "../hooks/useLocation";
import useApi from "../hooks/useApi";
import couponsInArea from "../api/couponsInArea";
const customData = require("../assets/map_styles/day_map.json");
function MapScreen(props) {
//Here is the location hook
var location = useLocation();
const couponBaseApi = useApi(couponsInArea.getCouponBases);
//Here is the API call that needs the location
useEffect(() => {
if (location) {
couponBaseApi.request(location.latitude, location.longitude);
}
}, []);
return (
<View style={styles.container}>
<MapView
followUserLocation={true}
zoomEnabled={true}
showsUserLocation={true}
style={styles.mapStyle}
provider={PROVIDER_GOOGLE}
customMapStyle={customData}
initialRegion={{
latitude: location.latitude,
longitude: location.longitude,
latitudeDelta: 0.0158,
longitudeDelta: 0.007,
}}
>
{couponBaseApi.data.couponBases.map((report) => (
<Marker
key={report.couponBaseID}
coordinate={{
latitude: report.latitude,
longitude: report.longitude,
}}
></Marker>
))}
</MapView>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: "#fff",
alignItems: "center",
justifyContent: "center",
},
mapStyle: {
width: Dimensions.get("window").width,
height: Dimensions.get("window").height,
},
});
export default MapScreen;
The solution was that the use effect hook needed to watch for a change on location,
so
useEffect(() => {
if (location) {
couponBaseApi.request(location.latitude, location.longitude);
}
}, [location]);
Thanks everyone!
Related
first i use react-native-maps to locate the expo app in toronto,and i use react-native-maps-directions to draw route between two location in toronto.MapView component works,but its child component MapViewDirections not working.no error notification.
"react-native-maps": "0.30.2",
"react-native-maps-directions": "1.8.0"
code like below:
import * as React from 'react';
import MapView from 'react-native-maps';
import { StyleSheet, Text, View, Dimensions } from 'react-native';
import { StatusBar } from 'expo-status-bar';
import MapViewDirections from 'react-native-maps-directions';
const LATITUDE = 43.653225;
const LONGITUDE = -79.383186;
export default function App() {
const origin = {latitude: 43.791680, longitude: -79.312770};
const destination = {latitude: 43.785230, longitude: -79.293420};
const GOOGLE_MAPS_APIKEY = 'myapikey';
return (
<View style={styles.container}>
<MapView provider="google"
initialRegion={{
latitude: LATITUDE,
longitude: LONGITUDE,
latitudeDelta: 0.0922,
longitudeDelta: 0.0421,
}}
style={styles.map} >
<MapViewDirections
origin={origin}
destination={destination}
apikey={GOOGLE_MAPS_APIKEY}
onError={(errorMessage) => {
console.log('GOT AN ERROR');
}}
/>
</MapView>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
alignItems: 'center',
justifyContent: 'center',
},
map: {
width: Dimensions.get('window').width,
height: Dimensions.get('window').height,
},
})
Finally,I found the solution after I check MapViewDirections's source code .MapViewDirections component has very special logic while using,this component constructed with react-native-maps,so it should be work with MapView.And secondly,its state should be shown after re-render,I use usestate to make its state rendered.I paste code here:
import * as React from 'react';
import MapView, { Marker } from 'react-native-maps';
import { StyleSheet, Text, View, Dimensions } from 'react-native';
import { StatusBar } from 'expo-status-bar';
import MapViewDirections from 'react-native-maps-directions';
import { useState } from 'react';
const LATITUDE = 43.653225;
const LONGITUDE = -79.383186;
export default function App() {
const [coordinates] = useState([
{
latitude: 43.791680, longitude: -79.312770,
},
{
latitude: 43.785230, longitude: -79.293420,
},
]);
const origin = {latitude: 43.791680, longitude: -79.312770};
const destination = {latitude: 43.785230, longitude: -79.293420};
const GOOGLE_MAPS_APIKEY = 'AIzaSyBpAexI1D_HPIrR9xz_RqAAKDNlYKmW0Q8';
return (
<View style={styles.container}>
<MapView
style={styles.maps}
initialRegion={{
latitude: coordinates[0].latitude,
longitude: coordinates[0].longitude,
latitudeDelta: 0.0622,
longitudeDelta: 0.0121,
}}>
<MapViewDirections
origin={coordinates[0]}
destination={coordinates[1]}
apikey={GOOGLE_MAPS_APIKEY} // insert your API Key here
strokeWidth={4}
strokeColor="#111111"
/>
<Marker coordinate={coordinates[0]} />
<Marker coordinate={coordinates[1]} />
</MapView>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
},
maps: {
width: Dimensions.get('screen').width,
height: Dimensions.get('screen').height,
},
})
What I'm trying to do is when the user clicks the position button in the map, to go to the user current location. For the location I'm using import * as Location from 'expo-location';
import React, { useState, useEffect } from 'react';
import MapView, { Marker, PROVIDER_GOOGLE } from 'react-native-maps';
import { StyleSheet, Image, Text, View } from 'react-native';
import * as Location from 'expo-location';
import { MaterialIcons } from '#expo/vector-icons';
import { LocationView, LocationBtn } from '../components/styles';
function Map(props) {
const [mapRegion, setMapRegion] = useState(null);
useEffect(() => {
(async () => {
const { status } = await Location.requestForegroundPermissionsAsync();
if (status !== 'granted') {
setErrorMsg('Permission to access location was denied');
return;
}
const location = await Location.getCurrentPositionAsync({});
setMapRegion({
longitude: location.coords.longitude,
latitude: location.coords.latitude,
longitudeDelta: 0.0922,
latitudeDelta: 0.0421,
});
console.log('location', location);
})();
}, []);
return (
<View style={{ flex: 1 }}>
<MapView
provider={PROVIDER_GOOGLE}
style={styles.map}
mapType={props.mapType}
// showsUserLocation={true}
loadingEnabled
initialRegion={mapRegion}
userInterfaceStyle="light"
showsTraffic
>
<Marker coordinate={mapRegion}>
<Image source={require('../assets/marker.png')} style={{ height: 90, width: 90 }} />
</Marker>
</MapView>
<LocationView>
<LocationBtn onPress={() => mapRegion}>
<MaterialIcons name="my-location" size={30} color="black" style={{ alignSelf: 'center', marginTop: 13 }} />
</LocationBtn>
</LocationView>
</View>
);
}
const styles = StyleSheet.create({
map: {
height: '100%',
},
});
export default Map;
By far I have put mapRegion state which it have the current location.
When I press the button, it doesn't work.
What do you mean by going to the user location? Looking at your code, you're saving your coordinates to the mapRegion state.
The onPress prop of LocationBtn has () => mapRegion which does nothing. It's just a function that has mapRegion return value.
What I'm trying to do is center the screen view to the user location when I press a custom button and not the default button react-native-map offers. I do get the longitude and latitude but I can't seem to figure out:
How to make the custom button call the method to center the view to the coordinates I have
How to center the View all together
import React,{ useState, useEffect} from 'react';
import MapView, {Marker, PROVIDER_GOOGLE} from 'react-native-maps';
import { StyleSheet, Image , Text, View} from 'react-native';
import * as Location from 'expo-location';
import {
LocationView ,
LocationBtn,
} from '../components/styles';
import { MaterialIcons } from '#expo/vector-icons';
const Map = (props) =>{
const [mapRegion, setMapRegion] = useState(null);
useEffect(() => {
(async () => {
let { status } = await Location.requestForegroundPermissionsAsync();
if (status !== 'granted') {
setErrorMsg('Permission to access location was denied');
return;
}
let location = await Location.getCurrentPositionAsync({});
setMapRegion({
longitude: location.coords.longitude,
latitude: location.coords.latitude,
longitudeDelta: 0.0922,
latitudeDelta: 0.0421
});
console.log('location', location)
})();
}, [])
return(
<View style={{ flex: 1}}>
<MapView
region={mapRegion}
provider={PROVIDER_GOOGLE}
style={styles.map}
mapType={props.mapType}
showsUserLocation={true}
showsMyLocationButton={true}
loadingEnabled={true}
initialRegion={mapRegion}
userInterfaceStyle='light'
showsTraffic={true}
>
<Marker
coordinate={mapRegion}
>
<Image source={require('../assets/marker.png')} style={{height: 90, width: 90}} />
</Marker>
</MapView>
<LocationView>
<LocationBtn
>
<MaterialIcons
name="my-location"
size={30}
color="black"
style={{alignSelf: 'center', marginTop: 13}}
/>
</LocationBtn>
</LocationView>
</View>
)
}
const styles = StyleSheet.create({
map: {
height: '100%',
},
})
export default Map;
<View style={{ flex: 1}}>
Please add style justify content and align items.
In component Description I have a button, when I press the button I send to Map component coordinates of marker:
const onNavigationTap = () => {
navigation.navigate('Map', {
destination: data["data"].coordinates,
});
}
In component Map I have condition:
const mapView = React.createRef();
if (route.params){
mapView.current.animateToRegion({
latitude: route.params.destination.latitude,
longitude: route.params.destination.longitude,
latitudeDelta: 0.4,
longitudeDelta: 0.4,
},1000);
}
return (
<MapView ref={mapView} />
)
So when I open Map I want to show region near marker. I've tried to create a button on Map screen:
<TouchableOpacity style={{
position: 'absolute',
top: '5%',
alignSelf: 'flex-start'
}} onPress={animateMap}><Text>Start</Text></TouchableOpacity>
and then I created function:
const animateMap = () => {
mapView.current.animateToRegion({ // Takes a region object as parameter
latitude: destination.latitude,
longitude: destination.longitude,
latitudeDelta: 0.4,
longitudeDelta: 0.4,
},1000);
}
And this solution with button on Map screen working fine but what I want is to animateToRegion not on button press, but when user opens the Map from Description component. I don't understand why in first case I got Null is not an object(evaluating 'mapView.current.animateToRegion'). Please tell me what should I do if I want to animateToRegion using params that I get from another component?
It seems that the error you got is because mapView.current.animateToRegion is null.
You can follow this sample code and code snippet below on how to achieve your use case:
App.js
import * as React from 'react';
import { Button, View, Text } from 'react-native';
import { NavigationContainer } from '#react-navigation/native';
import { createNativeStackNavigator } from '#react-navigation/native-stack';
import MapScreen from './Map'
function HomeScreen({ navigation }) {
const onBtnPress = () =>{
navigation.navigate('Maps', {
destination: { latitude: 40.463667, longitude: -3.74922 },
});
}
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Text>Home Screen</Text>
<Button
title="Go to Details"
onPress={onBtnPress}
/>
</View>
);
}
const Stack = createNativeStackNavigator();
function App() {
return (
<NavigationContainer>
<Stack.Navigator initialRouteName="Home">
<Stack.Screen name="Home" component={HomeScreen} />
<Stack.Screen name="Maps" component={MapScreen} />
</Stack.Navigator>
</NavigationContainer>
);
}
export default App;
Map.js
import React, { Component } from 'react';
import { Text, View, StyleSheet, Dimensions } from 'react-native';
import MapView, { Marker, Circle, Polyline } from 'react-native-maps';
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
alignItems: 'center',
justifyContent: 'center',
},
map: {
width: Dimensions.get('window').width,
height: Dimensions.get('window').height,
},
});
function Map (props){
const onMapLoad=()=>{
console.log(this.mapView)
console.log(props.route.params.destination.latitude)
this.mapView.animateToRegion({
latitude: props.route.params.destination.latitude,
longitude: props.route.params.destination.longitude,
latitudeDelta: 0.4,
longitudeDelta: 0.4,
},1000);
}
return (
<View style={styles.container}>
<MapView
ref={(ref) => this.mapView = ref}
style={styles.map}
initialRegion={{
latitude: 37.78825,
longitude: -122.4324,
latitudeDelta: 0.0922,
longitudeDelta: 0.0421,
}}
onMapReady={onMapLoad}
>
</MapView>
</View>
);
}
export default Map;
I am trying to make the map fit and fill the screen
The map just covers some part of the screen, but in this case I would like to have it fill the screen on any android device. I need someone to help.
My source code looks like this.
import React, { Component } from 'react';
import { View, StyleSheet, Text } from 'react-native';
import MapView , {Marker} from 'react-native-maps';
export default class ShowMap extends Component {
constructor(props) {
super(props);
this.state = {
};
}
render() {
return (
<View style={styles.container}>
<MapView
style={styles.map}
region={{
latitude: 6.406070,
longitude: 3.407350,
latitudeDelta: 0.015,
longitudeDelta: 0.0121,
}}
>
<Marker
coordinate={{ latitude: 6.406070, longitude: 3.407350 }}
title='Emi School of Engineering'
description='This is where the magic happens!'
></Marker >
</MapView>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
...StyleSheet.absoluteFillObject,
height: 660,
width: 420,
justifyContent: 'flex-start',
alignItems: 'stretch',
},
map: {
...StyleSheet.absoluteFillObject,
},
});
Looks like there is something i need to do, I am not doing correctly.
you can get the screen height from Dimension Interface provided by react-native and set replace height value from it.
import React, { Component } from 'react';
import { View, StyleSheet, Text, Dimensions } from 'react-native';
import MapView , {Marker} from 'react-native-maps';
//get screen height
const deviceHeight =Dimensions.get("window").height
export default class ShowMap extends Component {
constructor(props) {
super(props);
this.state = {
};
}
render() {
return (
<View style={styles.container}>
<MapView
style={styles.map}
region={{
latitude: 6.406070,
longitude: 3.407350,
latitudeDelta: 0.015,
longitudeDelta: 0.0121,
}}
>
<Marker
coordinate={{ latitude: 6.406070, longitude: 3.407350 }}
title='Emi School of Engineering'
description='This is where the magic happens!'
></Marker >
</MapView>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
...StyleSheet.absoluteFillObject,
height: deviceHeight,
width: 420,
justifyContent: 'flex-start',
alignItems: 'stretch',
},
map: {
...StyleSheet.absoluteFillObject,
},
});