setState in componentDidMount() not working in geolocation callback - javascript

I'm new to react-native and I'm trying to use geolocation to update the state.
In componentDidMount() I can call this.setState({lat: 1}); and it works. However if I call this.setState({lat: 1}); from within the geolocation.getCurrentPosition() callback the app crashes. I'm testing on a physical device.
I have stripped back everything to isolate the problem. Any help is greatly appreciated.
Here is my code:
import React, { Component } from 'react';
import { AppRegistry,ScrollView, ListView, Text, View } from 'react-native';
class TestProject extends Component {
constructor(props) {
super(props);
this.state = {
lat: 0
};
}
componentDidMount() {
navigator.geolocation.getCurrentPosition(function(position) {
this.setState({lat: 1}); // this causes a crash - without this line the app does not crash.
},
(error) => alert(error.message)
);
this.setState({lat: 1}); // this works
}
render() {
return (
<View>
<Text>
{this.state.lat}
</Text>
</View>
);
}
}
AppRegistry.registerComponent('TestProject', () => TestProject);

Thanks to #Phil I just had to set self = this:
let self = this;
navigator.geolocation.getCurrentPosition(function(position) { self.setState({ lat: 1 }); }, (error) => alert(error.message) );

Related

TypeError _this2.getWifiList().bind is not a function. React Native componentDidMount

I'm trying to get my app to refresh a select components list of options. The list will show a selection of wifi hotspots and I want the app to scan for them every 5 seconds, so I followed this guide: https://blog.stvmlbrn.com/2019/02/20/automatically-refreshing-data-in-react.html
But when I run the app, I get this error:
Error
Possible Unhandled Promise Rejection (id: 0):
TypeError: _this2.getWifiList().bind is not a function. (In '_this2.getWifiList().bind((0, _assertThisInitialized2.default)(_this2))', '_this2.getWifiList().bind' is undefined)
http://localhost:8081/index.bundle?platform=android&dev=true&minify=false:187369:75
tryCallOne#http://localhost:8081/index.bundle?platform=android&dev=true&minify=false:27870:16
http://localhost:8081/index.bundle?platform=android&dev=true&minify=false:27971:27
_callTimer#http://localhost:8081/index.bundle?platform=android&dev=true&minify=false:31410:17
_callImmediatesPass#http://localhost:8081/index.bundle?platform=android&dev=true&minify=false:31449:17
callImmediates#http://localhost:8081/index.bundle?platform=android&dev=true&minify=false:31666:33
__callImmediates#http://localhost:8081/index.bundle?platform=android&dev=true&minify=false:3610:35
http://localhost:8081/index.bundle?platform=android&dev=true&minify=false:3396:34
__guard#http://localhost:8081/index.bundle?platform=android&dev=true&minify=false:3593:15
flushedQueue#http://localhost:8081/index.bundle?platform=android&dev=true&minify=false:3395:21
flushedQueue#[native code]
invokeCallbackAndReturnFlushedQueue#[native code]
This error comes up 10 times in a second. I tried removing the bind but I get the same error.
MainScreen.js
import React, {useState} from 'react';
import { StyleSheet, PermissionsAndroid, Linking, View, TouchableOpacity } from 'react-native';
import { IndexPath, Layout, Text, Button, Select, SelectItem } from '#ui-kitten/components';
import { Icon } from 'react-native-eva-icons';
import {styles} from '../styles'
import WifiManager from "react-native-wifi-reborn";
class LocationSelector extends React.Component {
constructor(props) {
super(props);
this.state = {
selectedIndex : (new IndexPath(0)),
wifiList: null,
intervalID: null,
}
}
componentDidMount() {
this.getWifiList();
}
componentWillUnmount() {
clearInterval(this.intervalID);
}
getWifiList = async () => {
WifiManager.setEnabled(true);
await WifiManager.reScanAndLoadWifiList()
.then((data) => {
this.state.wifiList = data
this.intervalID = setTimeout( async () => {await this.getWifiList().bind(this)}, 5000);
});
}
renderOption = (title) => (
<SelectItem title={title}/>
)
render() {
console.log(this.state.selectedIndex);
const data = [
'Venue1',
'Venue2',
'Venue3',
];
const displayValue = data[this.state.selectedIndex.row];
return(
<Select
style={styles.select}
size="large"
placeholder='Default'
value={displayValue}
disabled={this.props.disabled}
accessoryLeft={PinIcon}
selectedIndex={this.state.selectedIndex}
onSelect={(index) => {(this.state.selectedIndex = index); this.forceUpdate()}}>
{data.map(this.renderOption)}
</Select>
);
}
}
class MainScreen extends React.Component {
constructor(props) {
super(props);
this.navigation = props.navigation
this.state = {
isCheckedIn: false,
}
}
render() {
return (
<Layout style={styles.container}>
<LocationSelector />
</Layout>
);
}
}
export default MainScreen;
The issue is in the way you are calling .bind():
this.getWifiList().bind(this)
Note that .bind() is a method of the Function prototype, therefore, is available only when called on a function reference.
In this case, you are calling .bind() on this.getWifiList(), which is the return value of getWifiList, not the functon itself.
To fix the error you are getting, you just need to call it on the function:
this.getWifiList.bind(this)

React Native Render an Object using map : undefined is not an object (evaluating 'currentLocation.coords')

I have a variable called currentLocation which is an object that I get from the navigator.geolocation.getCurrentPosition() method.
{"timestamp":1575687610918,"mocked":false,"coords":{"altitude":0,"heading":0,"longitude":72.9815203,"latitude":19.2076923,"speed":0,"accuracy":35.90299987792969}}
My question is how do I render it in a React Native app using map? I am getting the error : undefined is not an object (evaluating 'currentLocation.coords'). I want to be able to map over this object and simply display the data as text! I am new to React Native so any help would be greatly appreciated. Thank you!
Following is the code :
export default class HomeScreen extends React.Component {
constructor(props) {
super(props)
this.state =
{
currentLocation : '',
}
}
async componentDidMount() {
var location
setInterval(() => {
navigator.geolocation.getCurrentPosition(
position => {
location = JSON.stringify(position)
//console.log(location)
this.setState({ location })
},
error => Alert.alert(error.message),
{ enableHighAccuracy: true, timeout: 20000, maximumAge: 1000 }
);
this.setState({ currentLocation : location })
}, 1000)
}
render() {
var { currentLocation } = this.state
if(typeof(currentLocation) != undefined)
console.log('Inside render =========> ', currentLocation.coords)
return (
)
}
}
You want to loop over an object and display the data as text.You can use the Object.keys() for that.
Here's how to do it -
const p = {
"p1": "value1",
"p2": "value2",
"p3": "value3"
};
Object.keys(p).map((i) => {
console.log(i, p[i]) // i will be the key of object and p[i] will be the value of
key//
//return jsx using the above logic//
})
You can check out other ways to loop over an object here - How do I loop through or enumerate a JavaScript object?
Hope this helps you.
EDIT: -
The error is because your state is not an object. It's a string because you did this
location = JSON.stringify(position)
You can remove this stringify or inside render you can do
const currentLocation = JSON.parse(this.state.currentLocation)
You are probably looking for a package like this https://github.com/react-native-community/react-native-maps.
You can use MapView Component to render maps on your react-native app and use the Marker component to pin the current location. I think the documentation can help you.
If it's object
import * as React from 'react';
import { Text, View, StyleSheet } from 'react-native';
let obj = {"timestamp":1575687610918,"mocked":false,
"coords":{"altitude":0,"heading":0,"longitude":72.9815203,
"latitude":19.2076923,"speed":0,"accuracy":35.90299987792969
}}
export default class App extends React.Component {
render() {
const {timestamp, coords } = obj;
return (
<View>
<Text>{timestamp}</Text>
<Text>{coords.latitude}</Text>
<Text>{coords.longitude}</Text>
</View>
);
}
}
if it's array of objects
let arr = [{"timestamp":1575687610918,"mocked":false,
"coords":{"altitude":0,"heading":0,"longitude":72.9815203,
"latitude":19.2076923,"speed":0,"accuracy":35.90299987792969
}}]
export default class App extends React.Component {
render() {
return (
<View>
{arr && arr.map(a=>
<View key={Math.random()*10000}>
<Text>{a.timestamp}</Text>
<Text>{a.coords.latitude}</Text>
<Text>{a.coords.longitude}</Text>
</View>
)}
</View>
);
}
}

Expo - How to update state based on output of TaskManager

I would appreciate any guidance you might have regarding this topic, thank you in advance.
Here is my situation:
I have set up TaskManager to track the location of the device it is running on, which is working great. Where I am struggling is getting the data returned by TaskManager into my App component as state.
Here is a simplified example of my issue:
class App extends Component {
state = {}
// 'this' does not exist within a static method.
static updateState = (data) => this.setState(data)
render = () => { return <div>...</div> }
}
​
TaskManager.defineTask(LOCATION_TASK_NAME, ({ data, error }) => {
const { locations } = data // How can I get locations into state?
App.updateState(locations) // Won't work as I can't use 'this' within a static method!
})
The actual file looks like this:
import React from 'react'
import { StyleSheet, Text, View } from 'react-native'
import * as Location from 'expo-location'
import * as TaskManager from 'expo-task-manager'
import * as Permissions from 'expo-permissions'
const LOCATION_TASK_NAME = 'background-location-task'
export default class App extends React.Component {
state = {
latitude: 0,
longitude: 0,
}
static updateState(latitude, longitude) {
// updateState receives the data correctly, I just can't
// work out how to update the component state with values.
console.log(latitude, longitude)
// this.setState({ latitude, longitude }) // Doesn't work
}
notStaticUpdateState(latitude, longitude) {
// console.log(latitude, longitude) // won't work
}
async componentDidMount() {
const { status } = await Permissions.askAsync(
Permissions.LOCATION,
Permissions.USER_FACING_NOTIFICATIONS,
)
if (status === 'granted') {
await Location.startLocationUpdatesAsync(
LOCATION_TASK_NAME,
{
accuracy: Location.Accuracy.Highest,
distanceInterval: 1,
timeInterval: 1000,
},
)
}
}
render() {
const { latitude, longitude } = this.state
return (
<View style={styles.container}>
<Text>Lat: {latitude}</Text>
<Text>Lng: {longitude}</Text>
</View>
)
}
}
const styles = StyleSheet.create({
container: {
alignItems: 'center',
flex: 1,
justifyContent: 'center',
},
})
TaskManager.defineTask(LOCATION_TASK_NAME, ({ data, error }) => {
if (error) {
return
}
if (data) {
const { latitude, longitude } = data.locations[0].coords
App.updateState(latitude, longitude)
// App.notStaticUpdateState(latitude, longitude) // won't work
}
})

Re-render component with componentWillReceiveProps?

I have a .jsx with a parent class and a child, in the parent i initialize the api and stock the json content in a state:
constructor(props) {
super(props);
this.state = {
all: '',
};
}
componentDidMount() {
this.loadApi();
}
loadApi(){
this.setState({ all: myApiGet('https://********') });
}
After that i need to get the "url" of the differents pics for show them on the site. But there is the problem, I get the api json when i load the page and i don't success to re-load the function.
componentWillReceiveProps(nextProps) {
this.apiGetProductPicture(nextProps.categorie);
}
apiGetProductPicture = (i) => () => {
// TODO do something with the data
var stock = this.props.all
.then(stock => this.setState({ pictures: stock.content.categories[i].background }))
.catch(error => console.log('home2', error));
}
I try a lot of possibility and check the net but the solution doesn't work for me (or i just doesn't understand them ...)
Thanks for your time :/
Full component:
class ProductItem extends React.Component {
constructor(props) {
super(props);
this.state = {
pictures: '',
name: '',
price: '',
json: '',
};
//this.apiGetProductPicture = this.apiGetProductPicture.bind(this);
}
componentWillReceiveProps(nextProps) {
this.apiGetProductPicture(nextProps.categorie);
}
apiGetProductPicture = (i) => () => {
// TODO do something with the data
var stock = this.props.all
.then(stock => this.setState({ pictures: stock.content.categories[i].background }))
.catch(error => console.log('home2', error));
}
render() {
return (
......
)
}
}
Error message:
The above error occurred in the component:
in ProductItem (created by Home2)
in div (created by Home2)
in div (created by Home2)
in div (created by Home2)
in div (created by Home2)
in main (created by Home2)
in Home2
Consider adding an error boundary to your tree to customize error handling behavior.
You can learn more about error boundaries at https:// fb.me/react-error-boundaries.
react-dom.development.js:9312:5
ReferenceError: props is not defined
Ok i think i see some changes to be made
in your parent component your setting this.state.all to be a promise (the promise returned from your api call)
let's change that to be the actual json from your api call
Parent component:
constructor(props) {
super(props);
this.state = {
all: '',
};
this.loadApi = this.loadApi.bind(this);
}
componentDidMount() {
this.loadApi();
}
loadApi() {
myApiGet('https://********')
.then(all => this.setState({ all }));
}
Child Component:
class ProductItem extends React.Component {
constructor(props) {
super(props);
this.state = {
pictures: '',
name: '',
price: '',
json: '',
};
this.apiGetProductPicture = this.apiGetProductPicture.bind(this);
}
ComponetDidMount() {
apiGetProductPicture(this.props.categorie);
}
componentWillReceiveProps(nextProps) {
if (nextProps.categorie !== this.props.categorie)
{
this.apiGetProductPicture(nextProps.categorie);
}
}
apiGetProductPicture(categorie) {
// TODO do something with the data
if (!this.props.all) return;
var categories = (((this.props.all || {}).stock || {}).content || {}).categories;
if (categories.indexOf(categorie) > -1)
{
this.setState({ pictures: categories[categorie].background }));
}
}
render() {
return (
......
);
}
}
Thanks for your time :/
no worries :)
i se you posted "Lost in the javascriptception"
this and other questions have provided me with enough info to solve your problem, sorry the stackoverflow community was so mean to you, but not all of us are like that.
I would recommend in the future you post more info on your questions, like full code (except sensible stuff), not just parts, the codesanbox was the thing that let me test code and see where the problem was.
Also i f*** up on some of the previous answer, but to be fair i had very limited info to go along with, and most people answering won't test the code for tipos or stuff
version One
import React from "react";
import { render } from "react-dom";
import Hello from "./Hello";
const styles = {
fontFamily: "sans-serif",
textAlign: "center"
};
class ProductItem extends React.Component {
constructor(props) {
super(props);
this.state = {
pictures: '',
name: '',
price: '',
json: '',
};
this.apiGetProductPicture = this.apiGetProductPicture.bind(this);
}
componentDidMount() {
this.apiGetProductPicture(this.props.categorie);
}
componentWillReceiveProps(nextProps) {
this.apiGetProductPicture(nextProps.categorie);
}
apiGetProductPicture(categorie) {
// TODO do something with the data
var categories = this.props.send;
categorie = parseInt(categorie, 10);
if (categorie < categories.length) {
console.log(categories[categorie].background);
this.setState({ pictures: categories[categorie].background });
}
}
render() {
return (
<div>
<p>{this.props.name}</p>
<img src={this.state.pictures} />
</div>
);
}
}
export default class App extends React.Component {
constructor(props) {
super(props);
this.state = {
all: "",
categories: []
};
this.loadAPI = this.loadAPI.bind(this);
}
componentDidMount() {
this.loadAPI();
}
loadAPI() {
var test = fetch("https:*******")
.then(test => test.json())
.then(testJson => {
// alert(testJson.content.categories[0].description)
var obs = testJson.content.categories.slice();
// alert(testJson);
this.setState({ categories: obs });
});
}
render() {
return (
<div style={styles}>
<Hello name="CodeSandbox" />
<h1>Products</h1>
{this.state.categories.map( (value, i) => {
return <ProductItem
key={value.uid}
send={this.state.categories}
name={value.description}
categorie={i} />
})}
<h2>Start editing to see some magic happen {"\u2728"}</h2>
</div>
);
}
}
render(<App />, document.getElementById("root"));
My recommended Version
import React from "react";
import { render } from "react-dom";
import Hello from "./Hello";
const styles = {
fontFamily: "sans-serif",
textAlign: "center"
};
class ProductItem extends React.Component {
render() {
return (
<div>
<p>{this.props.name}</p>
<img src={this.props.picture} />
</div>
);
}
}
export default class App extends React.Component {
constructor(props) {
super(props);
this.state = {
all: "",
categories: []
};
this.loadAPI = this.loadAPI.bind(this);
}
componentDidMount() {
this.loadAPI();
}
loadAPI() {
var test = fetch("https:*****")
.then(test => test.json())
.then(testJson => {
// alert(testJson.content.categories[0].description)
var obs = testJson.content.categories.slice();
// alert(testJson);
this.setState({ categories: obs });
});
}
render() {
return (
<div style={styles}>
<Hello name="CodeSandbox" />
<h1>Products</h1>
{this.state.categories.map( (value, i) => {
return <ProductItem
key={value.uid}
picture={value.background}
name={value.description}
categorie={i} />
})}
<h2>Start editing to see some magic happen {"\u2728"}</h2>
</div>
);
}
}
render(<App />, document.getElementById("root"));
Hope this helps you out, don't be so hard on yourself, you know practice makes perfect, also would recommend you follow the react tutorial, to see what react is about, i can seam super hard and weird because it maybe a completely different programming model (it was for me), but when it click it's really cool

React-Native Error - "[TypeError: undefined is not an object (evaluating '_this2._OnAlert(count)')]"

I am trying to build a sample app which pops up an alert displaying the number of objects in the response of an API. The alert pops up once but afterwards, I am getting this error
[TypeError: undefined is not an object (evaluating '_this2._OnAlert(count)')]
This is my code
import React from 'react';
import { View, Button, Alert } from 'react-native';
import ProductListingItem from './ProductListingItem';
export default class ProductDetailedListingPage extends React.Component {
constructor() {
super();
}
_OnAlert(count){
console.log('_onAlert');
console.log('count = '+ count);
Alert.alert(
'API call success',
count+' objects',
[
{text:'done',onPress: () => { }}
]
);
}
_getResponseFromApi(){
console.log('_getREsponseFromApi called');
var myRequest = new Request('http://mysampleapi.com:8082/listProducts',);
myRequest.method = 'GET';
return fetch(myRequest)
.then((response)=> response.json())
.then((responseJson) => {
var data = responseJson.error;
console.log('data = '+data);
/*console.log(responseJson);*/
var count = Object.keys(responseJson).length;
count = '2';
console.log('count in _getResponseFromApi = '+count);
this._OnAlert(count);
return responseJson;
})
.catch((error) => {
console.log(error);
});
}
componentDidMount () {
console.log('componentDidMount');
this._getResponseFromApi();
}
componentWillMount () {
console.log('componentWillMount');
}
render(){
console.log('render');
return(
<View style = {{
flex:1,
flexDirection:'column',
}}>
<Button style ={{flex:1}}
onPress ={this._getResponseFromApi}
title ="Call"
/>
</View>
);
}
}
The problem is with _OnAlert() , it runs for the first time at componentDidMount() without issues and displays the alert dialog, but at the instance of onPress() it gives me the above mentioned error. I am a noob when it comes to React and Javascript, especially JSX. How can I fix this issue?
You should change your constructor and bind the method '_getResponseFromApi' to the scope here,
constructor() {
super();
this._getResponseFromApi = this._getResponseFromApi.bind(this);
}

Categories