I'm trying to understand how modal it works in React Native
What I want, it's when I click on marker, it display me the different information of specific marker (title, description, name).
What I have:
export default class MapsScreen extends React.Component {
state = { reports: [] }
componentDidMount() {
// call api
}
mapMarkers = () => {
return this.state.reports.map((report) => <Marker
key={report.key}
title={report.name}
coordinate={{ latitude: report.latitude, longitude: report.longitude }}
title={report.name}
description={report.description}
onPress={this._onMarkerPress.bind(this, this.state.reports.filter(r => r.key === report.key ))}
>
</Marker >)
}
render() {
return (
<MapView
style={{ ...StyleSheet.absoluteFillObject }}
initialRegion={{
latitude: 52.0,
longitude: -0.98,
latitudeDelta: 10,
longitudeDelta: 5
}} >
{this.mapMarkers()}
</MapView>
);
}
_onMarkerPress (markerData) {
console.log(JSON.stringify(markerData));
}
}
How can I start for do What I want ?
Thanks for your help :)
Related
I would like to update my map to show the user location. Using the code below, I get a world map not a map of the UK, which is what the latitude and longitude should show, can anyone help?
const [location, setLocation] = useState({
initialPosition: {
latitude: 0,
longitude: 0,
latitudeDelta: 0,
longitudeDelta: 0,
},
});
const getLocationPermissions = async () => {
const granted = await request(
Platform.select({
android: PERMISSIONS.ANDROID.ACCESS_COARSE_LOCATION,
ios: PERMISSIONS.IOS.LOCATION_WHEN_IN_USE,
}),
{
title: 'DemoApp',
message: 'App would like access to your location ',
}
);
return granted === RESULTS.GRANTED;
};
useEffect(() => {
// check permission
const isGranted = getLocationPermissions();
if (isGranted) {
// get location
Geolocation.getCurrentPosition((info) => {
let lat = info.coords.latitude;
let long = info.coords.longitude;
// update state with location,latitude: 52.62869394486038
//longitude: -1.9794797216434805
var initialRegion = {
latitude: lat,
longitude: long,
latitudeDelta: 0.0922,
longitudeDelta: 0.0421,
};
});
} else {
console.log('error');
}
}, []);
return (
<View style={styles.container}>
<MapView style={styles.map} initialRegion={location.initialPosition} />
</View>
);
I am guessing you are using react-native-maps MapView Component, it has a showsUserLocation boolean property.
<MapView
style={styles.map}
showsUserLocation={true}
initialRegion={location.initialPosition}
/>
Perhaps this would resolve it?
After retrieve user location from device, animate map to that region.
import * as React from "react";
import { Text, View, StyleSheet } from "react-native";
import MapView from "react-native-maps";
const App = () => {
const mapRef = React.useRef(null);
React.useEffect(() => {
// Below are mocked location in UK. Retrieve real location
// from device with Geoocation API
// latitude: 52.62869394486038
//longitude: -1.9794797216434805
const userRegion = {
latitude: 52.62869394486038,
longitude: -1.9794797216434805,
latitudeDelta: 0.0922,
longitudeDelta: 0.0421,
};
mapRef.current?.animateToRegion(userRegion);
}, []);
return (
<View style={styles.container}>
<MapView
ref={mapRef}
initialRegion={{
latitude: 37.78825,
longitude: -122.4324,
latitudeDelta: 0.0922,
longitudeDelta: 0.0421,
}}
style={styles.mapStyle}
/>
</View>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
alignItems: "center",
justifyContent: "center",
paddingTop: 10,
backgroundColor: "#ecf0f1",
},
mapStyle: {
...StyleSheet.absoluteFillObject,
},
});
export default App;
Working example snack.
https://snack.expo.dev/#emmbyiringiro/9f04a3
I'm trying to display markers based on radius and I'm unable to develop a logic for that. My locations for markers come from firestore geopoint. I have successfully filter the marker based on category from my firestore but i'm unable filter it by maximum distance radius
MY problem: I dont know how to use radius property and where to put that property and based on radius how to filter the markers
class mapView extends Component {
constructor() {
super()
this.state = {
users:[],
initialPosition: {
latitude: -6.64064,
longitude: 106.8273983,
latitudeDelta: 0,
longitudeDelta: 0
},
}
}
getLocation(){
Geolocation.getCurrentPosition(
(position) => {
var lat = position.coords.latitude
var long = position.coords.longitude
var initialRegion ={
latitude: lat,
longitude: long,
latitudeDelta: LATITUDE_DELTA,
longitudeDelta: LONGITUDE_DELTA,
}
this.setState({initialPosition: initialRegion})
},
(error) => {
// See error code charts below.
console.log(error.code, error.message);
},
{ enableHighAccuracy: true, timeout: 15000, maximumAge: 10000 }
);
}
fetchRequests(){
this.subscriber = firebase.firestore()
.collection("users").onSnapshot(docs => {
let users = []
docs.forEach(doc => {
users.push(doc.data())
})
this.setState({users})
console.log(users)
})
}
componentDidMount() {
this.props.fetchUser();
this.fetchRequests();
this.getLocation();
}
render() {
return (
<View style={styles.container}>
<MapView
showsUserLocation={true}
provider={PROVIDER_GOOGLE}
style={styles.map}
initialRegion={this.state.initialPosition}>
{this.state.users.filter(element => element.golDarah === this.props.currentUser.golDarah && element.geo_point.latitude
!== this.props.currentUser.geo_point.latitude && element.locationOn === true )
.map((user, idx) => <Marker
key={idx}
coordinate={{latitude: user.geo_point.latitude, longitude: user.geo_point.longitude}}
>
<Callout>
<View style={{height: 0.1 * windowHeight()}}>
<Text>Nama: {user.name}</Text>
<Text>Golongan Darah: {user.golDarah}</Text>
<TouchableOpacity onPress={()=>{Linking.openURL(`${user.noHp}`)}}>
<Text>No HP:{user.noHp}</Text>
</TouchableOpacity>
</View>
</Callout>
</Marker>)}
</MapView>
</View>
);
}
}
I am using react-native-maps and I am rendering multiple markers using array.map(). I also have a carousel which is displaying some pictures and info about each marker.
Basically whenever a user clicks on an item in the carousel, the map zooms in to that marker and displays the callout by calling the following function.
markers[index].showCallout()
which will display the call out for that marker. So I need to populate the array with refs so that I can pass in the index which will show the callout when the user moves the carousel.
To start I created an array which will hold some state.
const [markers, setMarkers] = useState([]);
Here is the array.map function which generates all the markers on the map.
{searchResults.map((marker, index) => {
return (
<MapView.Marker
key={marker.key}
ref={(m) => {
setMarkers((markers) => [[...markers], m]);
}}
onPress={() => onMarkerPress(marker, index)}
coordinate={{
latitude: marker.geopoint.latitude,
longitude: marker.geopoint.longitude,
}}>
<Callout>
<Text>{marker.price}</Text>
</Callout>
</MapView.Marker>
);
})}
The error that I am getting is "Error: Maximum update depth exceeded. This can happen when a component repeatedly calls setState inside componentWillUpdate or componentDidUpdate. React limits the number of nested updates to prevent infinite loops."
because I am trying to populate the array using this line.
ref={(m) => {setMarkers((markers) => [[...markers], m])}}
Not sure if there is another way to do this.
The below trick seems to work fine if you're using class components
ref={ref => this.state.markers[index] = ref}
but I am using functional components in my app.
Here's the entire code
const HomeScreen = ({navigation}) => {
{
/************ Array for Rendering Items in Carousel */
}
const searchResults = [
{
key: '1',
photographerURL:
'https://images.pexels.com/photos/598917/pexels-photo-598917.jpeg?auto=compress&cs=tinysrgb&dpr=2&h=650&w=940',
price: 100,
photographerName: 'Brittany',
geopoint: {latitude: 38.8929372, longitude: -77.0025119},
},
{
key: '2',
photographerURL:
'https://images.pexels.com/photos/1264210/pexels-photo-1264210.jpeg?auto=compress&cs=tinysrgb&dpr=1&w=500',
price: 200,
photographerName: 'Franklin',
geopoint: {latitude: 38.8963932, longitude: -77.0030197},
},
{
key: '3',
photographerURL:
'https://images.pexels.com/photos/243757/pexels-photo-243757.jpeg?auto=compress&cs=tinysrgb&dpr=1&w=500',
price: 50,
photographerName: 'Bryan',
geopoint: {latitude: 38.8925739, longitude: -76.9987113},
},
];
/**
* Function to animate map.
* When the user moves the carousel, the map will zoom in to
* the region where that photographer is
*/
const mapRef = useRef(null);
const [carouselRef, setCarouselRef] = useState([]);
const [markers, setMarkers] = useState([]);
console.log(markers);
/**
* Zooms in on the region whenever the carousel item is changed
*/
const onCarouselItemChange = (index) => {
let location = searchResults[index];
{
mapRef.current.animateToRegion({
latitude: location.geopoint.latitude,
longitude: location.geopoint.longitude,
//Zoom Level
latitudeDelta: 0.01,
longitudeDelta: 0.01,
});
}
//markers[index].showCallout();
};
/**
* When a user clicks on the marker, the carousel jumps
* to that particular item
*/
const onMarkerPress = (location, index) => {
mapRef.current.animateToRegion({
latitude: location.geopoint.latitude,
longitude: location.geopoint.longitude,
//Zoom Level
latitudeDelta: 0.01,
longitudeDelta: 0.01,
});
carouselRef.snapToItem(index);
};
return (
<View style={styles.container}>
{/**************** Google Maps *************** */}
<MapView
style={styles.map}
ref={mapRef}
provider={PROVIDER_GOOGLE}
showsUserLocation
followsUserLocation
initialRegion={{
latitude: 38.8929372,
longitude: -77.0025119,
//Zoom Level
latitudeDelta: 0.01,
longitudeDelta: 0.03,
}}>
{searchResults.map((marker, index) => {
return (
<MapView.Marker
key={marker.key}
//ref={(m) => [setMarkers((markers) => [...markers, m])]}
onPress={() => onMarkerPress(marker, index)}
coordinate={{
latitude: marker.geopoint.latitude,
longitude: marker.geopoint.longitude,
}}>
<Callout>
<Text>{marker.price}</Text>
</Callout>
</MapView.Marker>
);
})}
</MapView>
{/***************** Carousel ***********/}
<Carousel
ref={(c) => {
setCarouselRef(c);
}}
data={searchResults}
horizontal
containerCustomStyle={styles.carousel}
onSnapToItem={(index) => onCarouselItemChange(index)}
layout={'default'}
//layoutCardOffset={`50`}
sliderWidth={screenWidth}
itemWidth={screenWidth * 0.8}
itemHeight={400}
pagingEnabled={true}
showsHorizontalScrollIndicator={false}
renderItem={({item}) => (
<Item
photographerName={item.photographerName}
uri={item.photographerURL}
price={item.price}
/>
)}
keyExtractor={(item) => item.key}
/>
</View>
);
};
const styles = StyleSheet.create({
container: {
...StyleSheet.absoluteFill,
},
map: {
...StyleSheet.absoluteFill,
},
carousel: {
position: 'absolute',
bottom: 0,
marginBottom: '5%',
},
});
export default HomeScreen;
I know this topic is old, but I found the solution.
It's due to the functional components architecture in react-native.
The function is called multiple times, so the limit of setState is rapidly reached.
To avoid this, add:
const [markersLoaded,setMarkersLoaded]=useState(false)
Then:
ref={(m) => {
if (!markersLoaded)
{
setMarkers((markers) => [[...markers], m]);
setMarkersLoaded(true);}
}}
I have a <MapView> and the region never updates when I scroll the map. The map is essentially stuck on one region. When I scroll it seems to pass the exact same region to this.props.onRegionChange which prevents the map from scrolling. How do I make it pass in a scrolled to region?
<MapView
style={styles.map}
region={this.props.region}
onRegionChange={this.props.onRegionChange}
>
{
this.props.markers.map(marker => {
return (
<MapView.Marker
coordinate={{ latitude: marker.latitude, longitude: marker.longitude }}
title={marker.name}
/>
)
})}
</MapView>
full code:
Map.js
import { StyleSheet } from 'react-native'
import React, { Component } from 'react'
import MapView from 'react-native-maps'
import { connect } from 'react-redux'
import {
Button,
Container
} from 'native-base'
import selectMarkers from './markers.selector'
import { updateRegion } from './map.action'
import Icon from 'react-native-vector-icons/FontAwesome'
import { toggleMenu } from '../search-page/searchPage.action'
import mapStyle from './style'
const mapStateToProps = (state) => ({
region: state.get('map').get('region'),
markers: selectMarkers(state)
})
const mapDispatchToProps = (dispatch) => ({
onRegionChange: (region) => {
dispatch(updateRegion(region))
},
onToggleMenuClick: () => {
dispatch(toggleMenu())
}
})
class Map extends Component {
componentDidMount() {
const { store } = this.context
this.unsubscribe = store.subscribe(() => { })
}
componentWillUnmount() {
this.unsubscribe()
}
render() {
console.log('map')
console.log('markers', this.props.markers)
return (
<Container>
<MapView
style={styles.map}
region={this.props.region}
onRegionChange={this.props.onRegionChange}
>
{
this.props.markers.map(marker => {
return (
<MapView.Marker
coordinate={{ latitude: marker.latitude, longitude: marker.longitude }}
title={marker.name}
/>
)
})}
</MapView>
<Button
small
icon
style={mapStyle.toggleMenuButton}
onPress={() => this.props.onToggleMenuClick()}>
<Icon name="sliders" size={20} color="#FFFFFF" />
</Button>
</Container>
)
}
}
Map.contextTypes = {
store: React.PropTypes.object
}
Map.propTypes = {
region: React.PropTypes.shape({
latitude: React.PropTypes.number,
longitude: React.PropTypes.number,
latitudeDelta: React.PropTypes.number,
longitudeDelta: React.PropTypes.number
}).isRequired,
onRegionChange: React.PropTypes.func.isRequired,
onToggleMenuClick: React.PropTypes.func.isRequired,
markers: React.PropTypes.array
}
export default connect(
mapStateToProps,
mapDispatchToProps
)(Map)
const styles = StyleSheet.create({
map: {
...StyleSheet.absoluteFillObject,
zIndex: -1
}
})
map.reducer
// #flow
import initialState from '../../config/config'
import { UPDATE_REGION } from './map.action'
const map = (
initialMapState: [] = initialState.get('map'),
action: Object): string => {
switch (action.type) {
case UPDATE_REGION: {
if (action.payload) {
return initialMapState.set('region', {
latitudeDelta: 0.0922,
longitudeDelta: 0.0421,
latitude: action.payload.region.latitude,
longitude: action.payload.region.longitude
})
} else {
return initialMapState
}
}
default:
return initialMapState
}
}
export default map
Use onRegionChangeComplete
<MapView
style={styles.map}
region={this.props.region}
onRegionChangeComplete={this.props.onRegionChange}>
The examples in the docs use onRegionChange. So maybe onRegionChange works for most. Maybe it works without redux. Anyway easy fix is onRegionChangeComplete if getting the same issue.
I want to set a marker on MapView in React Native, but I can't find any information through official documents of MapView.
If it is not allowed in that way, how can I use existed react module such as react-googlemaps in React Native?
Thank you #naoufal.
Finally I am able to display markers on map for react native IOS.
<View style={styles.container}>
<MapView style={styles.map}
initialRegion={{
latitude: 37.78825,
longitude: -122.4324,
latitudeDelta: 0.0,
longitudeDelta: 0.0,
}}
>
<MapView.Marker
coordinate={{latitude: 37.78825,
longitude: -122.4324}}
title={"title"}
description={"description"}
/>
</MapView>
</View>
For map and container styles, I have used following styles:
var styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#F5FCFF',
},
map: {
position: 'absolute',
top: 0,
left: 0,
right: 0,
bottom: 0,
}
});
I have referred following link:
React native IOS- Display Markers on map
You can set markers using the annotations prop.
For example:
var markers = [
{
latitude: 45.65,
longitude: -78.90,
title: 'Foo Place',
subtitle: '1234 Foo Drive'
}
];
<MapView
region={...}
annotations={markers}
/>
import MapView, { Marker } from 'react-native-maps';
<View>
<MapView
style={styles.mapStyle}
initialRegion={{
latitude: 37.78825,
longitude: -122.4324,
latitudeDelta: 0.0922,
longitudeDelta: 0.0421,
}}
>
<Marker coordinate = {{latitude: 37.78825,longitude: -122.4324}}
pinColor = {"purple"} // any color
title={"title"}
description={"description"}/>
</MapView>
</View>
For those looking for a dynamic (e.g., from API), rather than hardcoded coordinates; here's the copy/paste snippet from one of the screens automatically generated by Expo CLI, included React Hooks:
import axios from "axios";
import MapView from "react-native-maps";
import { Dimensions, StyleSheet } from "react-native";
import { Marker } from "react-native-maps";
import { Text, View } from "../components/Themed";
export default function TabTwoScreen() {
const [markers, setMarkers] = useState(null);
useEffect(() => {
axios({
method: "GET",
url: "https://API_URL...",
headers: {
Authorization: `Bearer MY_TOKEN_ABC123...`,
Accept: "application/json",
},
})
.then((response) => {
setMarkers(response.data.results);
})
.catch((error) => {
console.log(error);
});
}, []); // "[]" makes sure the effect will run only once.
return (
<View style={styles.container}>
<Text style={styles.title}>Tab Two</Text>
<MapView style={styles.map}>
{markers &&
markers.map((marker: any, index: number) => (
<Marker
key={index}
coordinate={{
latitude: marker.location[1],
longitude: marker.location[0],
}}
title={marker.title}
description={marker.description}
/>
))}
</MapView>
</View>
);
}
const styles = StyleSheet.create({
// Add your styles here...
});
Updated code - 2020 | React Native 0.63
import MapView, { Marker } from "react-native-maps";
<MapView
style={styles.mapStyle}
region={{
latitude: this.state.mapLat,
longitude: this.state.mapLong,
latitudeDelta: 0.001663,
longitudeDelta: 0.002001,
}}
onRegionChangeComplete={this.onRegionChange}
>
<Marker
coordinate={{latitude: 51.5078788, longitude: -0.0877321}}
>
</Marker>
</MapView>
According to this issue, it's not exposed yet. You won't be able to use the web-React package either, React Native doesn't work like that.
Two suggestions:
Wait for the above issue to be resolved to get a proper API
Try and use WebView to link to a Google Map with an annotation
I'm not actually certain whether #2 is possible though.
import MapView from 'react-native-maps';
<View style={StyleSheet.absoluteFillObject}>
<MapView style={styles.map}
showsUserLocation //to show user current location when given access
loadingEnabled //to show loading while map loading
style={styles.map}
initialRegion={{
latitude,
longitude,
latitudeDelta: 0.0922,
longitudeDelta: 0.0421,
}}
>
{
locations && locations.map((location, index) => {
const {
coords: { latitude, longitude }
} = location;
return (
<MapView.Marker
key={index}
coordinate={{ latitude, longitude }}
title={"title"}
description={"address"}
// onPress={this.onMarkerPress(location)}
/>
)
})
}
</MapView>
</View>```
const styles = StyleSheet.create({
map: {
position: 'absolute',
top: 0,
left: 0,
right: 0,
bottom: 0,
}
});
"dependencies": {
"native-base": "^2.13.8",
"react": "16.9.0",
"react-native": "0.61.2",
"react-native-maps": "git+https://git#github.com/react-native-community/react-native-maps.git"
},
Try using this. Hope it helps to mark for many locations. Happy Coding.
Thank you #zia_qureshi. Finally I am able to display markers on map for react native android.
import MapView, { Marker } from "react-native-maps";
const App = () => {
const [region, setRegion] = useState({
latitude: 51.5078788,
longitude: -0.0877321,
latitudeDelta: 0.009,
longitudeDelta: 0.009
});
return (
<MapView
style={{ flex: 1 }}
region={region}
onRegionChangeComplete={region => setRegion(region)}
>
<Marker coordinate={{ latitude: 51.5078788, longitude: -0.0877321 }} />
</MapView>
);
};
export default App;