Passing location value to another component in React native - react-native

I am trying to send the latitude and longitude values(fetched using navigator.geolocation in home screen) to the detail screen, but instead of actual values, null value is being received by the details page.This is my code:
import React from 'react';
import { View, Text, Button } from 'react-native';
import { createStackNavigator } from 'react-navigation';
class HomeScreen extends React.Component {
constructor(props) {
super(props);
this.state = {
latitude: null,
longitude: null,
error: null,
};
}
render() {
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Button title="Add New Outlet" onPress={() => {
navigator.geolocation.getCurrentPosition(
(position) => {
this.setState({
latitude: position.coords.latitude,
longitude: position.coords.longitude,
error: null,
});
},
(error) => this.setState({ error: error.message }),
{ enableHighAccuracy: true, timeout: 20000, maximumAge: 1000 },
);
this.props.navigation.navigate('Details', {latitude: this.state.latitude, longitude: this.state.longitude,});
}}/>
</View>
);
}
}
class DetailsScreen extends React.Component {
render() {
const { navigation } = this.props;
const latitude = navigation.getParam('latitude', 'NO-ID');
const longitude = navigation.getParam('longitude', 'some default value');
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Text>Details Screen</Text>
<Text>latitude: {JSON.stringify(latitude)}</Text>
<Text>longitude: {JSON.stringify(longitude)}</Text>
</View>
);
}
}
const RootStack = createStackNavigator(
{
Home: HomeScreen,
Details: DetailsScreen,
},
{
initialRouteName: 'Home',
}
);
export default class App extends React.Component {
render() {
return <RootStack />;
}
}
The values are getting passed successfully between the components but current state values are not getting passed.Please help...

This can be an issue of the asynchronous nature of this.setState(). Try modifying the code a little bit. Instead of calling this.props.navigation.navigate() after the this.setState(), try calling it within the this.setState() in a callback as a second parameter. Here is how you can do it:
this.setState({
latitude: position.coords.latitude,
longitude: position.coords.longitude,
error: null,
}, () => {
this.props.navigation.navigate('Details', {latitude: this.state.latitude, longitude: this.state.longitude,})
});
I hope that helps. Let me know if it works.

Related

React Native render error: null is not an object (evaluating origin.location)

I'm fairly new to react-native and decided to try cloning an app, following a tutorial. I'm trying to pass data stored in redux to a map rendered on my home screen but when I run, I keep getting the following error: null is not an object (evaluating origin.location). Any suggestions, solutions and quick fixes are welcome. Thanks
Home screen codes
import { View, TouchableOpacity, Text } from 'react-native'
import { React } from 'react'
import HeaderTabs from '../components/HeaderTabs'
import { GooglePlacesAutocomplete } from 'react-native-google-places-autocomplete'
import { Ionicons } from '#expo/vector-icons'
import { setDestination, setOrigin } from '../slices/navSlice'
import { useDispatch } from 'react-redux'
import { SafeAreaView } from 'react-native-safe-area-context'
import { GOOGLE_MAPS_APIKEY } from '#env'
import Map from './components/Map'
const Home = ({navigation}) =>{
const dispatch = useDispatch();
return (
<SafeAreaView style={{ backgroundColor:"white",
flex:1,
padding:5,
marginTop:-20
}}>
<View style={{alignItems:"flex-end"}} >
<TouchableOpacity onPress={() => {navigation.navigate("CustomerInfo")}}>
<Ionicons name="person-circle" size={30} color="black" />
</TouchableOpacity>
</View>
<HeaderTabs />
<View style={{marginTop:15, flexDirection:"row"}}>
<GooglePlacesAutocomplete
query={{key: GOOGLE_MAPS_APIKEY}}
enablePoweredByContainer={false}
onPress={(data, details = null) => {
dispatch(setOrigin({
location: details.geometry.location,
description: data.description,
})
);
dispatch(setDestination(null));
}}
fetchDetails={true}
returnKeyType={"search"}
placeholder='Enter package pickup location'
debounce={400}
nearbyPlacesAPI='GooglePlacesSearch'
styles={{
textInput:{
backgroundColor:"#eee",
borderRadius:15,
fontFamily:"Poppins_500Medium",
marginTop:10,
fontSize:18,
},
textInputContainer:{
backgroundColor:"#eee",
borderRadius:15,
flexDirection:"row",
alignItems:"center",
width:380
},
}}
renderLeftButton={() => (
<View style={{
marginLeft:10
}}>
<Ionicons name="location" size={20} color="green" />
</View>
)}
renderRightButton={() => (
<View style={{
flexDirection:"row",
marginRight:5,
backgroundColor:"white",
padding:9,
borderRadius:30,
alignItems:"center"
}}>
<Ionicons name="search" size={15} color="black" />
</View>
)}
/>
</View>
<Map />
</SafeAreaView>
)};
export default Home;
Map component codes
import { View, Text } from 'react-native'
import React from 'react'
import MapView, { Marker } from 'react-native-maps'
import { useSelector } from 'react-redux'
import { selectOrigin } from '../slices/navSlice'
const Map = () => {
const origin = useSelector(selectOrigin);
return (
<MapView
style={{flex:1, marginTop:10}}
initialRegion={{
latitude: origin.location.lat,
longitude: origin.location.lng,
latitudeDelta: 0.0922,
longitudeDelta: 0.0421,
}}
/>
);
};
export default Map;
Where origin.location is coming from
const initialState = {
origin: null,
destination: null,
travelTimeInformation: null
}
export const navSlice = createSlice({
name: "nav",
initialState,
reducer: {
setOrigin: (state, action) => {
state.origin = action.payload;
},
setDestination: (state, action) => {
state.destination = action.payload;
},
setTravelTimeInformation: (state, action) => {
state.travelTimeInformation = action.payload;
},
},
});
export const { setOrigin, setDestination, setTravelTimeInformation } = navSlice.actions;
export const selectOrigin = (state) => state.nav.origin;
export const selectDestination = (state) => state.nav.destination;
export const selectTravelTimeInformation = (state) => state.nav.travelTimeInformation;
export default navSlice.reducer;
Issue
The origin state value is initially null.
const initialState = {
origin: null, // <-- initially null here
destination: null,
travelTimeInformation: null
}
And the code is attempting to access into this null value.
const Map = () => {
const origin = useSelector(selectOrigin);
return (
<MapView
style={{flex:1, marginTop:10}}
initialRegion={{
latitude: origin.location.lat,
longitude: origin.location.lng,
latitudeDelta: 0.0922,
longitudeDelta: 0.0421,
}}
/>
);
};
Solutions
You've a few options. Here are 4, there may be others.
Conditionally render the MapView component on the origin state being populated.
const Map = () => {
const origin = useSelector(selectOrigin);
return origin?.location
? (
<MapView
style={{flex:1, marginTop:10}}
initialRegion={{
latitude: origin.location.lat,
longitude: origin.location.lng,
latitudeDelta: 0.0922,
longitudeDelta: 0.0421,
}}
/>
) : null;
};
Use Optional Chaining operator on the origin access and provide a fallback value.
const Map = () => {
const origin = useSelector(selectOrigin);
return (
<MapView
style={{flex:1, marginTop:10}}
initialRegion={{
latitude: origin?.location?.lat ?? 0, // use fallback value that makes sense
longitude: origin?.location?.lng ?? 0, // use fallback value that makes sense
latitudeDelta: 0.0922,
longitudeDelta: 0.0421,
}}
/>
);
};
Provide a valid, defined origin value from the selector.
const defaultOrigin = { location: { lat: 0, lng: 0 } }; // use origin value that makes sense
export const selectOrigin = (state) => state.nav.origin ?? defaultOrigin;
Or just provide a more useful initial state value.
const defaultOrigin = { location: { lat: 0, lng: 0 } }; // use origin value that makes sense
const initialState = {
origin: defaultOrigin,
destination: null,
travelTimeInformation: null
}

Undefined is not an object in React native when rendering

I'm new to React and React native and am trying to retrieve data from an API and then render it but I seem to be running into problems.
I'm getting errors like:
undefined is not an object (evaluating 'attractions.map') in RenderPhotos_render
I may have jumped into React Native too early...So excuse my lack of knowledge!
import React, {Component} from 'react';
import {StyleSheet, View, ActivityIndicator} from 'react-native';
import MapView, {Marker} from 'react-native-maps';
import {connect} from 'react-redux';
const mapStateToProps = state => {
return {
attractions: state.attractions.attractions,
};
};
const mapDispatchToProps = dispatch => {
return {
GET_Attractions(callback) {
dispatch({type: 'attractions/GET_Attractions', callback});
},
};
};
export default connect(
mapStateToProps,
mapDispatchToProps,
)(
class Home extends Component {
state = {
loading: true,
};
componentDidMount = () => {
const callback = () => {
this.setState({
loading: false,
});
};
this.props.GET_Attractions(callback);
};
renderMapMarker = () => {
const {attractions} = this.props;
return attractions.map(marker => {
return (
<Marker
key={marker.caseId}
title={marker.caseName}
description="點擊查看詳細資料"
coordinate={{
latitude: marker.latitude,
longitude: marker.longitude,
}}
/>
);
});
};
render() {
if (this.state.loading) {
return (
<View style={styles.container}>
<ActivityIndicator />
</View>
);
} else {
return (
<View style={styles.container}>
<MapView
style={styles.mapView}
initialRegion={{
latitude: 24.149706,
longitude: 120.683813,
latitudeDelta: 8,
longitudeDelta: 8,
}}>
{this.renderMapMarker()}
</MapView>
</View>
);
}
}
},
);
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
},
mapView: {
width: '100%',
height: '100%',
},
});
By default attractions might be undefined so you can check to validate first then render if it has data to render like
render() {
if (this.state.loading) {
return (
...
);
} else {
return (
<View style={styles.container}>
<MapView
...
>
{this.props.attractions && this.renderMapMarker()} // and this
</MapView>
</View>
);
}
}
You are trying to use a prop "attractions" that is being populated from the reducer, so initially when your screen is being rendered the prop "attractions" will be undefined so you need to achieve this with the condition to make the error go away.
{attractions && this.renderMapMarker()}
Just add ? after attractions and it should work.
renderMapMarker = () => {
const {attractions} = this.props;
return attractions?.map(marker => {
return (
<Marker
key={marker.caseId}
title={marker.caseName}
description="點擊查看詳細資料"
coordinate={{
latitude: marker.latitude,
longitude: marker.longitude,
}}
/>
);
});
};

How to resolve the Can't perform a React state update on an unmounted component.! Always show this whenever i display Map

I have two-component classes. First is TrackRoute1.js from which I'm passing bus location parameters and also user current location parameters to class Dispaly.js through the server. But after displaying I keep getting a memory leak that indicates that it cant perform react state update on an unmounted component. I tried everything. But it won't help.!
TrackRoute1.js
import React, { Component } from "react";
import {
ActivityIndicator,
FlatList,
Text,
View,
TouchableHighlight,
StyleSheet,
} from "react-native";
import AnimatedRegion from "react-native-maps";
import Display1 from "./Display1";
import { LogBox } from "react-native";
import CustomHeader from "../CustomHeader";
LogBox.ignoreLogs(["Setting a timer"]);
import * as Location from 'expo-location';
export default class App extends Component {
constructor(props) {
super(props);
this.state = {
data: [],
isLoading: true,
userLat: 0,
userLng: 0,
routeCoordinates: [],
distanceTravelled: 0,
prevLatLng: {},
coordinate: new AnimatedRegion({
userLat: 0,
userLng: 0,
latitudeDelta: 0,
longitudeDelta: 0
})
};
}
componentDidMount() {
this.interval = setInterval(() => this.getCoords(), 1000);
this._getLocationAsync();
}
async getCoords() {
fetch("https://api.thingspeak.com/channels/1323137/feeds.json?results=1")
.then((response) => response.json())
.then((json) => {
console.log(json.feeds, "API res");
this.setState({ data: json.feeds });
})
.catch((error) => console.error(error))
.finally(() => {
this.setState({ isLoading: false });
});
}
_getLocationAsync = async () => {
console.log("123")
let { status } = await Location.requestForegroundPermissionsAsync();
console.log(status)
if (status !== 'granted') {
console.log('Permission to access location was denied');
return;
}
// Old method //
let location = await Location.getCurrentPositionAsync({});
console.log(location, "my location");
this.setState({
userLat: Number(location.coords.latitude),
userLng: Number(location.coords.longitude)
})
console.log(this.state.userLng,"userLng111")
// New Method //
// this.location = await Location.watchPositionAsync(
// {
// enableHighAccuracy: true,
// distanceInterval: 1,
// timeInterval: 1000
// },
// newLocation => {
// let { coords } = newLocation;
// // console.log(coords);
// this.setState({
// userLat: Number(coords.latitude),
// userLng: Number(coords.longitude)
// })
// },
// error => console.log(error)
// );
// return this.location;
};
calcDistance = newLatLng => {
const { prevLatLng } = this.state;
return haversine(prevLatLng, newLatLng) || 0;
};
componentWillUnmount() {
clearInterval(this.interval);
navigator.geolocation.clearWatch(this.watchID);
}
render() {
const { data, isLoading } = this.state;
return (
<View style={{ flex: 1, width: "100%" }}>
<CustomHeader title="Track Route" navigation={this.props.navigation} />
{isLoading ? (
<ActivityIndicator />
) : (
<FlatList
data={data}
keyExtractor={(id, index) => index.toString()}
renderItem={({ item }) => (
<Display1
value1={item.field1}
value2={item.field2}
value3={item.field3}
value4={this.state.userLat}
value5={this.state.userLng}
/>
)}
/>
)}
</View>
);
}
}
const styles = StyleSheet.create({
appButtonText: {
fontSize: 18,
color: "#fff",
fontWeight: "bold",
alignSelf: "center",
textTransform: "uppercase",
},
});
Dispaly.js
import React, { Component } from "react";
import {
StyleSheet,
View,
Text,
TouchableOpacity,
Platform,
Dimensions,
Image,
} from "react-native";
import * as Location from 'expo-location';
import MapView, {
PROVIDER_GOOGLE,
Marker,Polyline,
AnimatedRegion,
} from "react-native-maps";
import AutoScrolling from "react-native-auto-scrolling";
import { getPreciseDistance } from 'geolib';
const { width, height } = Dimensions.get("window");
export default class Map extends Component {
// constructor(props) {
// super(props);
// this.state = {
// };
// }
constructor(props) {
super(props);
this.marker = null;
this.timeout = null;
this.state = {
isMapReady: false,
userLat: 0,
userLng: 0,
Time:0,
Dis: 0,
hasLocationPermissions: false,
locationResult: null,
coordinate: new AnimatedRegion({
latitude: parseFloat(this.props.value1),
longitude: parseFloat(this.props.value2),
latitudeDelta: 0.05,
longitudeDelta: 0.04,
}),
};
}
async componentDidMount() {
this.mounted = true;
this.timeout = setTimeout(()=>{ var pdis = getPreciseDistance(
{latitude:parseFloat(this.props.value1), longitude: parseFloat(this.props.value2)},
{latitude: parseFloat(this.props.value4), longitude: parseFloat(this.props.value5)},
);
// console.log(
// `Precise Distance\n\n${pdis} Meter\nOR\n${pdis / 1000} KM`
// );
this.setState({Dis:pdis,Time:Math.round( pdis/(this.props.value3*1000) )})} ,2000)
this.setState({
Time:this.state.Time.toFixed(2)
})
}
componentWillUnmount = () =>{
this.mounted = false;
if (this.timeout) {
clearTimeout(this.timeout)
}
}
onMapLayout = () => {
this.setState({ isMapReady: true });
};
render() {
// console.log( this.state.userLat,
// this.state.userLng,"myConsole")
return (
<View>
{this.state.Dis !== 0 ?
<View style={{backgroundColor:"rgba(52, 52, 52, 0.00006)"}}>
<Text style={{ color: "green", fontWeight: "bold", marginTop: 10, alignSelf: "center", marginBottom: 3, }}>Bus is {this.state.Dis / 1000} km Away.</Text>
<AutoScrolling style={{ backgroundColor: "rgba(52, 52, 52, 0.00006)",
width: 400,
padding: 10,
marginBottom: 10,}} endPadding={50}>
<Text style={ {
color: "red",
fontSize: 20,
fontWeight: "bold",
textAlign: "center",
margin: 5,
}}>Bus is Coming in {this.state.Time} Minutes</Text>
</AutoScrolling>
</View>
:null }
<MapView
initialRegion={{
latitude: parseFloat(this.props.value1),
longitude: parseFloat(this.props.value2),
latitudeDelta: 0.02,
longitudeDelta: 0.01,
}}
onMapReady={this.onMapLayout}
provider={PROVIDER_GOOGLE}
loadingIndicatorColor="#e21d1d"
ref={(map) => (this.map = map)}
style={{
width,height
}}
loadingEnabled={true}
>
{this.state.isMapReady && (
<MapView.Marker
key="AIzaSyB8k3Irk81q1k8pbj5tPM33KRWNdtROoOg"
identifier="marker"
coordinate={{
latitude: parseFloat(this.props.value1),
longitude: parseFloat(this.props.value2),
}}
flat={false}
title="Route 1"
description = "Driver: Muhammad Murtaza"
>
<Image source={require("D:/React Native apps/Bus-Track1-main/assets/car-marker.png")} style={{ width: 60, height: 50 }}/>
</MapView.Marker>
)}
<MapView.Marker
key="AIzaSyArnXt8Xqydc3BF9Udt8JCkFuKqgPR_HM0"
identifier="marker1"
coordinate={{
latitude: parseFloat(this.props.value4),
longitude: parseFloat(this.props.value5)
}}
title="My Location"
/>
<Polyline
coordinates={[
{ latitude: parseFloat(this.props.value4),
longitude: parseFloat(this.props.value5)
},
{ latitude: parseFloat(this.props.value1),
longitude: parseFloat(this.props.value2)
},
]}
strokeColor="red"
strokeColors={[
'#238C23',
'red'
]}
strokeWidth={5}
/>
</MapView>
</View>
);
}
}
I have two-component classes. First is TrackRoute1.js from which I'm passing bus location parameters and also user current location parameters to class Dispaly.js through the server. But after displaying I keep getting a memory leak that indicates that it cant perform react state update on an unmounted component. I tried everything. But it won't help.!

Having problems rendering contents from ComponentDidMount to <View>

How do i go about this? I am trying to render the contents of Initial region, so that once the Application Loads it automatically gets the Longitude and Latitude of the Location and put on the screen.
I am just a bit confused as to what and how to go about it, My source code is given thus
import React, { Component } from 'react';
import { View, Text , StyleSheet, Dimensions } from 'react-native';
import MapView from 'react-native-maps';
export default class HomePage extends Component {
constructor(props) {
super(props);
}
componentDidMount(){
navigator.geolocation.getCurrentPosition((position)=>{
var lat = parseFloat(position.coords.latitude)
var long = parseFloat(position.coords.longitude)
var initialRegion ={
latitude: lat,
longitude: long,
latitudeDelta: 0.0922,
longitudeDelta: 0.0421
}
this.setState({ initialRegion: initialRegion })
},
(error) => aalert(JSON.stringify(error)),
{
enableHighAccuracy: true, timeout: 20000, maximumAge: 1000
});
}
render() {
return (
<View style={styles.container}>
<MapView style={styles.map}
initialRegion={this.state.initialRegion}
showsUserLocation={true}>
</MapView>
</View>
);
}
}
const styles=StyleSheet.create({
container:{
flex: 1,
},mapContainer: {
flex: 1,
},
map: {
flex: 1,
width: Dimensions.get("window").width,
height: Dimensions.get("window").height,
}
});
Any candid advice as to how I can do something like that? Kindly assist.
I would guess the your map loads with no initialRegion as you are doing a request after the first render (componentDidMount) you may try to prevent the map from loading before you have the necessary information, I've also started your state on constructor. The code would look like this:
import React, { Component } from 'react';
import { View, Text , StyleSheet, Dimensions } from 'react-native';
import MapView from 'react-native-maps';
export default class HomePage extends Component {
constructor(props) {
super(props);
this.state = {
initialRegion: null
}
}
componentDidMount(){
navigator.geolocation.getCurrentPosition((position)=>{
var lat = parseFloat(position.coords.latitude)
var long = parseFloat(position.coords.longitude)
var initialRegion = {
latitude: lat,
longitude: long,
latitudeDelta: 0.0922,
longitudeDelta: 0.0421
}
this.setState({ initialRegion: initialRegion })
},
(error) => aalert(JSON.stringify(error)),
{
enableHighAccuracy: true, timeout: 20000, maximumAge: 1000
});
}
render() {
return (
<View style={styles.container}>
{this.state.initialRegion !== null && (
<MapView style={styles.map}
initialRegion={this.state.initialRegion}
showsUserLocation={true} />
)}
</View>
);
}
}
Wish success on your project.

Expo - React Native GeoFence with API Data

I am trying to work on a simple proof of concept for geofencing around certain latitude and longitudes
Structure: - location service is turned on (no need background updates for now just initial location) - user location is determined - looks at data of locations in json file and finds the ones in close proximity #10km (refer to URL for API)
EXPO has recently updated the SDK to manage this, but with looking through multiple searches nothing really comes up with managing a simple task of placing static location to a database and then runs the proximity radius.
Using a basic setup for the concept but not sure next steps... Below is the setup that I have for now and would like some direction on where the GeoFence calls begins
import React, {Component} from 'react';
import { AppRegistry, Text, View, StyleSheet, FlatList, Image } from 'react-native';
import { Constants, MapView, Location, Permissions } from 'expo';
export default class App extends Component {
constructor() {
super ()
this.state = {
dataSource: []
}
}
state = {
mapRegion: null,
hasLocationPermissions: false,
locationResult: null
};
_handleMapRegionChange = mapRegion => {
console.log(mapRegion);
this.setState({ mapRegion });
};
_getLocationAsync = async () => {
let { status } = await Permissions.askAsync(Permissions.LOCATION);
if (status !== 'granted') {
this.setState({
locationResult: 'Permission to access location was denied',
});
} else {
this.setState({ hasLocationPermissions: true });
}
let location = await Location.getCurrentPositionAsync({});
this.setState({ locationResult: JSON.stringify(location) });
// Center the map on the location we just fetched.
this.setState({mapRegion: { latitude: location.coords.latitude, longitude: location.coords.longitude, latitudeDelta: 0.0922, longitudeDelta: 0.0421 }});
};
renderItem = ({item}) => {
return (
<View>
<View>
<Text>{item.code}
<Text>{item.name}
<Text>{item.lat}
<Text>{item.lon}
</Text>
</Text>
</Text>
</Text>
</View>
</View>
)}
componentDidMount() {
this._getLocationAsync();
//const url = 'https://www.json-generator.com/api/json/get/ccLAsEcOSq?indent=1'
const url = 'https://gist.githubusercontent.com/tdreyno/4278655/raw/7b0762c09b519f40397e4c3e100b097d861f5588/airports.json'
fetch(url)
.then((repsonse) => repsonse.json())
.then((responseJson) => {
this.setState({
dataSource: responseJson
})
})
.catch((error) => {
console.log(error)
})
}
render() {
return (
<View style={styles.container}>
<Text style={styles.paragraph}>
Pan, zoom, and tap on the map!
</Text>
{
this.state.locationResult === null ?
<Text>Finding your current location...</Text> :
this.state.hasLocationPermissions === false ?
<Text>Location permissions are not granted.</Text> :
this.state.mapRegion === null ?
<Text>Map region doesn't exist.</Text> :
<MapView
style={{ alignSelf: 'stretch', height: 200 }}
region={this.state.mapRegion}
onRegionChange={this._handleMapRegionChange}
/>
}
<Text>
Location: {this.state.locationResult}
</Text>
<View style={styles.container}>
<FlatList
//={[{ key: 'a'}, { key: 'b'}]}
//renderItem={({item }) => <Text>{item.key}</Text>}
data={this.state.dataSource}
renderItem={this.renderItem}
/>
</View>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
paddingTop: Constants.statusBarHeight,
backgroundColor: '#ecf0f1',
padding: 8,
},
paragraph: {
margin: 24,
fontSize: 18,
fontWeight: 'bold',
textAlign: 'center',
},
});
This is only proof of concept and just need some directions which will help me move on. Links and Tutorials anything will help
Thanks (still learning)