Create an array of refs (Maximum update depth exceeded) - javascript

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);}
}}

Related

react-native not updating MapView initialRegion with react-native-geolocation-service

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

New modal onpress marker in react native

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 :)

filter marker based on radius react native

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>
);
}
}

Dynamically Rendering MapView Marker React Native

This is how my Map View Looks like :
<MapView
style={styles.mapStyle}
initialRegion={{
latitude: 51.524300,
longitude: -0.037790,
latitudeDelta: 0.0122,
longitudeDelta: 0.0421,
}}
>
<Marker
coordinate={{
latitude:51.524464,
longitude:-0.036285,
latitudeDelta:0.0,
longitudeDelta:0.0
}}
title={"Marker 1"}
description={customData.text1}
/>
<Marker
coordinate={{
latitude:51.524310,
longitude:-0.037798,
latitudeDelta:0.0,
longitudeDelta:0.0
}}
title={"Marker 2"}
description={customData.text2}
/>
<Marker
coordinate={{
latitude:51.523174,
longitude:-0.039332,
latitudeDelta:0.0,
longitudeDelta:0.0
}}
title={"Marker 3"}
description={customData.text3}
/>
</MapView>
As you can see I have hard-coded 3 markers just to make sure that the MapViews works properly. Now, I use an external API which tells the parking spaces based on the user's input. So for example if the user enter's a certain postcode/ ZIP code, the api returns an array where I have various information about the parking space such as longitude and latitude. I want to represent these parking spaces with markers on the map. I use a FlatList for that:
<FlatList
data = {parkingSpaces}
renderItem={({ item }) => {
return <MapView.Marker
coordinate={{
latitude: parseInt(item.latitude),
longitude:parseInt(item.longitude)
}}
title = {"parking markers"}
/>
}}
keyExtractor={item => item.unique_identifier}
/>
I don't get any errors, however I can't see the markers on the map. Below is my full code
import React ,{Component,useState,useEffect} from 'react'
import {View,Text,StyleSheet,Dimensions,Button,Alert,FlatList} from 'react-native'
import MapView , {Marker}from 'react-native-maps'
import axios from 'axios'
import { TextInput } from 'react-native-gesture-handler'
import camdenParking from '../api/camdenParking'
import SearchBar from '../components/SearchBar'
/// Key Secret : 5ulg30lagu2o493uwsmn24rxklsg0ivb05k2zl6xqhiz8js9e7
/// App Secret Token : NTEX14nQLbrS8MIz4RmC6riUV6K2aQ_j687H
const HomeScreen = ()=>
{
const [parkingSpaces,setparkingSpaces] = useState([])
const[term,setTerm] = useState('')
let userLatitude = 0
let userLongitude = 0
const customData = require("./MarkersText.json")
const searchApi = async() => {
const response = await camdenParking.get("",{
params:{
//longitude:-0.115444,
//latitude:51.517597
postcode: term
}
}) // you can change this later on
console.log(response.data)
setparkingSpaces(response.data)
console.log(term)
}
const findCoordinates = () => {
navigator.geolocation.getCurrentPosition(
position => {
const locationString = JSON.stringify(position); // Here we get the JSON object but it needs to be parsed
var longLat = JSON.parse(locationString); // Here we parse the JSON object
userLatitude=longLat.coords.latitude
userLongitude=longLat.coords.longitude
console.log(userLatitude) // This prints the current latitude from the user
console.log(userLongitude) // This prints the longitude
},
error => Alert.alert(error.message),
{ enableHighAccuracy: false, timeout: 20000, maximumAge: 1000 }
);
};
//useEffect(()=>{
// searchApi()
//},[])
return(
<View style={styles.container}>
<SearchBar
term={term}
onTermChange={newTerm=>setTerm(newTerm)}
onTermSubmit={()=> searchApi(term)}
/>
<MapView
style={styles.mapStyle}
initialRegion={{
latitude: 51.524300,
longitude: -0.037790,
latitudeDelta: 0.0122,
longitudeDelta: 0.0421,
}}
>
<Marker
coordinate={{
latitude:51.524464,
longitude:-0.036285,
latitudeDelta:0.0,
longitudeDelta:0.0
}}
title={"Marker 1"}
description={customData.text1}
/>
<Marker
coordinate={{
latitude:51.524310,
longitude:-0.037798,
latitudeDelta:0.0,
longitudeDelta:0.0
}}
title={"Marker 2"}
description={customData.text2}
/>
<Marker
coordinate={{
latitude:51.523174,
longitude:-0.039332,
latitudeDelta:0.0,
longitudeDelta:0.0
}}
title={"Marker 3"}
description={customData.text3}
/>
</MapView>
<Text>We have found {parkingSpaces.length} results</Text>
<Button onPress={searchApi} title=" Click Here To Get Parking Spaces" />
<Button onPress={findCoordinates} title=" Click Here To Get User's Location" />
<Text>Parking Spaces found around {term}</Text>
<FlatList
data = {parkingSpaces}
renderItem={({ item }) => {
return <MapView.Marker
coordinate={{
latitude: parseInt(item.latitude),
longitude:parseInt(item.longitude)
}}
title = {"parking markers"}
/>
}}
keyExtractor={item => item.unique_identifier}
/>
<FlatList
data = {parkingSpaces}
renderItem={({ item }) => {
return <Text> {item.road_name} | Possible Available Spaces:{item.parking_spaces} </Text>
}}
keyExtractor={item => item.unique_identifier}
/>
</View>
);
};
const styles = StyleSheet.create(
{
container:{
flex:1,
backgroundColor: '#fff',
//alignItems: 'center',
//justifyContent: 'center',
//...StyleSheet.absoluteFillObject,
//marginLeft:0,
//height:400,
//width:400,
//justifyContent:"flex-end",
//alignItems:"center",
},
mapStyle: {
width: 400,
height:400,
//width: Dimensions.get('window').width,
//height: Dimensions.get('window').height,
},
}
)
export default HomeScreen
UPDATE
This is how my code looks like now:
import React ,{Component,useState,useEffect} from 'react'
import {View,Text,StyleSheet,Dimensions,Button,Alert,FlatList} from 'react-native'
import MapView , {Marker}from 'react-native-maps'
import axios from 'axios'
import { TextInput } from 'react-native-gesture-handler'
import camdenParking from '../api/camdenParking'
import SearchBar from '../components/SearchBar'
/// Key Secret : 5ulg30lagu2o493uwsmn24rxklsg0ivb05k2zl6xqhiz8js9e7
/// App Secret Token : NTEX14nQLbrS8MIz4RmC6riUV6K2aQ_j687H
const HomeScreen = ()=>
{
const [parkingSpaces,setparkingSpaces] = useState([])
const[term,setTerm] = useState('')
let userLatitude = 0
let userLongitude = 0
const customData = require("./MarkersText.json")
const searchApi = async() => {
const response = await camdenParking.get("",{
params:{
//longitude:-0.115444,
//latitude:51.517597
postcode: term
}
}) // you can change this later on
console.log(response.data)
setparkingSpaces(response.data)
console.log(term)
}
const findCoordinates = () => {
navigator.geolocation.getCurrentPosition(
position => {
const locationString = JSON.stringify(position); // Here we get the JSON object but it needs to be parsed
var longLat = JSON.parse(locationString); // Here we parse the JSON object
userLatitude=longLat.coords.latitude
userLongitude=longLat.coords.longitude
console.log(userLatitude) // This prints the current latitude from the user
console.log(userLongitude) // This prints the longitude
},
error => Alert.alert(error.message),
{ enableHighAccuracy: false, timeout: 20000, maximumAge: 1000 }
);
};
//useEffect(()=>{
// searchApi()
//},[])
return(
<View style={styles.container}>
<SearchBar
term={term}
onTermChange={newTerm=>setTerm(newTerm)}
onTermSubmit={()=> searchApi(term)}
/>
<MapView
style={styles.mapStyle}
initialRegion={{
latitude: 51.524300,
longitude: -0.037790,
latitudeDelta: 0.0122,
longitudeDelta: 0.0421,
}}
>
{parkingSpaces.map((val, index) => {
return (<MapView.Marker
coordinate={{
latitude: parseInt(val.latitude),
longitude:parseInt(val.longitude)
}}
key={index}
title = {"parking markers"}
/>);
})}
</MapView>
<Text>We have found {parkingSpaces.length} results</Text>
<Button onPress={searchApi} title=" Click Here To Get Parking Spaces" />
<Button onPress={findCoordinates} title=" Click Here To Get User's Location" />
<Text> WC2A 3PD</Text>
<Text>Parking Spaces found around {term}</Text>
<FlatList
data = {parkingSpaces}
renderItem={({ item }) => {
return <MapView.Marker
coordinate={{
latitude: parseInt(item.latitude),
longitude:parseInt(item.longitude),
latitudeDelta:0.0,
longitudeDelta:0.0
}}
title = {"parking markers"}
description = {"parking"}
/>
}}
keyExtractor={item => item.unique_identifier}
/>
<FlatList
data = {parkingSpaces}
renderItem={({ item }) => {
return <Text> {item.road_name} | Possible Available Spaces:{item.parking_spaces} </Text>
}}
keyExtractor={item => item.unique_identifier}
/>
</View>
);
};
const styles = StyleSheet.create(
{
container:{
flex:1,
backgroundColor: '#fff',
//alignItems: 'center',
//justifyContent: 'center',
//...StyleSheet.absoluteFillObject,
//marginLeft:0,
//height:400,
//width:400,
//justifyContent:"flex-end",
//alignItems:"center",
},
mapStyle: {
width: 400,
height:400,
//width: Dimensions.get('window').width,
//height: Dimensions.get('window').height,
},
}
)
export default HomeScreen
The first problem is the use of FlatList as container for your markers. The solution is to map over your parkingSpaces array in between your MapView. The second problem is that you call parseInt on your coordinates, which causes the map to not render your markers at all. In addition you would lose precision.
Code:
<MapView
style={styles.mapStyle}
initialRegion={{
latitude: 51.524300,
longitude: -0.037790,
latitudeDelta: 0.0122,
longitudeDelta: 0.0421,
}}
>
{parkingSpaces.map((val, index) => {
return (<MapView.Marker
coordinate={{
latitude: val.latitude,
longitude: val.longitude
}}
key={index}
title = {"parking markers"}
/>);
})}
</MapView>
Working Demo:
https://snack.expo.io/#tim1717/mature-croissant

React-native : how to get coordinates and delta of visible area in MapView component

Say I have a map defined in react native this way :
render() {
return (
<View style={{flex: 1, position: 'relative'}}>
<MapView
style={styles.map}
showsUserLocation={this.state.showsUserLocation}
region={{
latitude: 48.8534100,
longitude: 2.3378000,
latitudeDelta: 0.12,
longitudeDelta: 0.065
}}
onRegionChangeComplete={
() => {
// How to retrieve coordinates and Delta when user moves the map
console.log(this.region);
}
} />
</View>
);
}
How to retrieve the coordinates and Delta when the user moves on the map ?
Here is one way to do this...
In your render() function, set onRegionChangeComplete like this:
onRegionChangeComplete={this.onRegionChangeComplete}
Then define the callback function in your component, e.g. below the render() function:
onRegionChangeComplete: function(region) {
console.log(region.longitude);
console.log(region.latitude);
}

Categories