MQTT Response with React Native - json

I am currently making an IoT App that I'm trying to connect to a raspberry pi using MQTT. I use the react_native_mqtt package. I use the code posted by simaAttar from here: How to use MQTT on React Native? . The problem I have it doesnt always update my code when I alter it and save. What I'm trying to achieve is to receive a JSON formatted string from the rasp and use that to fill a FlatList with react-native-elements ListItem. But the data received is undefined, even though yesterday I did have it working for a second, it won't work anymore now.
Any help is appreciated.
EDIT: this is the code (forgot to post it)
import React, {Component} from 'react';
import init from 'react_native_mqtt';
import AsyncStorage from '#react-native-community/async-storage';
import {
ActivityIndicator,
StyleSheet,
Text,
View,
FlatList,
TouchableOpacity,
TextInput,
Button,
Alert,
} from 'react-native';
import {ListItem} from 'react-native-elements';
init({
size: 10000,
storageBackend: AsyncStorage,
enableCache: true,
sync: {},
});
export default class AllPeople extends Component {
constructor() {
super();
this.onMessageArrived = this.onMessageArrived.bind(this);
this.onConnectionLost = this.onConnectionLost.bind(this);
const client = new Paho.MQTT.Client(
'onto.mywire.org',
8080,
'Client-' + Math.random() * 9999 + 1,
);
client.onMessageArrived = this.onMessageArrived;
client.onConnectionLost = this.onConnectionLost;
client.connect({
onSuccess: this.onConnect,
useSSL: false,
onFailure: (e) => {
console.log('Error: ', e);
},
});
this.state = {
message: [''],
data: [],
isLoading: true,
client,
messageToSend: '',
isConnected: false,
};
}
onConnect = () => {
console.log('Connected');
const {client} = this.state;
client.subscribe('/app/to/allpeople');
this.setState({
isConnected: true,
error: '',
isLoading: true,
messageToSend: 'allpeople',
});
this.sendMessage();
};
onMessageArrived(entry) {
console.log("Nieuwe Test 1:")
console.log("PayloadMEssage: "+entry.payloadMessage);
console.log("Payload tostring: "+entry.payloadMessage.toString())
//this.setState({data: entry, isLoading: false});
}
sendMessage = () => {
console.log('message send.');
var message = new Paho.MQTT.Message(this.state.messageToSend);
message.destinationName = '/app/from';
if (this.state.isConnected) {
this.state.client.send(message);
} else {
this.connect(this.state.client)
.then(() => {
this.state.client.send(message);
this.setState({error: '', isConnected: true});
})
.catch((error) => {
console.log(error);
this.setState({error: error});
});
}
};
onConnectionLost(responseObject) {
if (responseObject.errorCode !== 0) {
console.log('onConnectionLost:' + responseObject.errorMessage);
this.setState({error: 'Lost Connection', isConnected: false});
}
}
render() {
return (
<View style={styles.container}>
{this.state.isLoading ? (
<ActivityIndicator />
) : (
<FlatList
keyExtractor={keyExtractor}
data={this.state.data}
renderItem={Item}
/>
)}
</View>
);
}
}
const keyExtractor = (item, index) => login.username.toString();
const Item = ({item}) => {
return (
<TouchableOpacity>
<ListItem
title="TestTitle" //{item.results.name.first}
titleStyle={{fontWeight: 'bold'}}
leftIcon={<MCIcons name="account" size={36} color="dodgerblue" />}
subtitle={item.results.name.last}
rightTitle={item.results.registered.date}
rightTitleStyle={{fontSize: 14}}
chevron
bottomDivider
/>
</TouchableOpacity>
);
};
const styles = StyleSheet.create({
container: {
backgroundColor: '#F5FCFF',
},
});

I have managed to fix it by changing entry.payloadMessage to entry.payloadString. Apparentyly I changed it during the editing of my code.

Related

React Native Download JSON but not displaying in Flatlist

I am trying to download the JSON. React Native is downloading the json but I am not sure why Flatlist is not displaying the items. If I change the data={dummyData} in flatlist to data={MyList} then, the flatlist is able to display.
let viewableItemsChanged = null;
const dummyData = GrabData('http://hunterdata.serveblog.net/10record.json');
const MyList = [
{"id":"0","title":"MyBook0","url":"URLBook-0","image":"image-0" },
{"id":"1","title":"MyBook1","url":"URLBook-1","image":"image-1" },
{"id":"2","title":"MyBook2","url":"URLBook-2","image":"image-2" },
{"id":"3","title":"MyBook3","url":"URLBook-3","image":"image-3" },
{"id":"4","title":"MyBook4","url":"URLBook-4","image":"image-4" },
{"id":"5","title":"MyBook5","url":"URLBook-5","image":"image-5" }];
async function GrabData(TheURL) {
let abc = [];
try {
let response = await fetch(TheURL, {headers: {'Cache-Control' : 'no-cache'}});
let responseJson = await response.json();
console.log(responseJson);
return responseJson;
} catch (error) {
console.error(error);
}
}
const renderItem = ({item}) => {
return <View><Text>{item.title}</Text></View>
}
const List = () => {
return (
<FlatList
style={styles.list}
data={dummyData}
renderItem={renderItem}
/>
)
};
there is an issue with your code. you are calling the async function without await keyword. so it returns undefine response like this. {"_U": 0, "_V": 0, "_W": null, "_X": null}
Please Try this solution.
import React, { useEffect , useState } from 'react';
import { SafeAreaView, View, FlatList, StyleSheet, Text, StatusBar } from 'react-native';
const Item = ({ title }) => (
<View style={styles.item}>
<Text style={styles.title}>{title}</Text>
</View>
);
const App = () => {
const [data, setData] = useState([])
useEffect(() => {
apicall();
},[])
const apicall = async () => {
let dd = await GrabData("http://hunterdata.serveblog.net/10record.json");
setData(dd)
}
const GrabData = async (TheURL) => {
try {
let response = await fetch(TheURL, {headers: {'Cache-Control' : 'no-cache'}});
let responseJson = await response.json();
return responseJson;
} catch (error) {
console.error(error);
}
}
const renderItem = ({ item }) => (
<Item title={item?.title} />
);
return (
<SafeAreaView style={styles.container}>
<FlatList
data={data}
renderItem={renderItem}
keyExtractor={item => item?.id}
/>
</SafeAreaView>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
marginTop: StatusBar.currentHeight || 0,
},
item: {
backgroundColor: '#f9c2ff',
padding: 20,
marginVertical: 8,
marginHorizontal: 16,
},
title: {
fontSize: 32,
},
});
export default App;
You should activate the functions in componentDidMount also try states instead of const

I have this issue undefined is not an object (evaluating 'this.state.dataSource.map')

I want to display a list of places from a online json url.
import React, { Component } from "react";
import {
View,
StyleSheet,
Dimensions,
Image,
StatusBar,
TextInput,
TouchableOpacity,
Text,
Button,
Platform,
Alert,
FlatList,
ActivityIndicator,
} from "react-native";
let url = "https://cz2006api.herokuapp.com/api/getAll";
let url2 = "";
export default class ClinicComponent extends Component {
constructor(props) {
super(props);
this.state = {
isLoading: true,
dataSource: null,
};
}
componentDidMount() {
return fetch("https://cz2006api.herokuapp.com/api/getAll")
.then((response) => response.json())
.then((responseJson) => {
this.setState({
isLoading: false,
dataSource: responseJson.data.data,
});
})
.catch((error) => {
console.log(error);
});
}
render() {
if (this.state.isLoading) {
return (
<View style={styles.container}>
<ActivityIndicator />
</View>
);
} else {
let hospitals = this.state.dataSource.map((val, key) => {
return (
<View key={key} style={styles.item}>
<Text>{val.name}</Text>
</View>
);
});
return (
<View style={styles.item}>
{/* <Text>Content Loaded</Text> */}
{hospitals}
</View>
);
}
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: "#fff",
alignItems: "center",
justifyContent: "center",
},
item: {
flex: 1,
alignSelf: "stretch",
margin: 10,
alignItems: "center",
justifyContent: "center",
borderBottomWidth: 1,
borderBottomColor: "#eee",
},
});
Unfortunately when i tried to run this via expo cli I got an error, saying undefined is not an object
enter image description here
Can anyone help me pls!!! I would just like to have a list of hospitals which are scrollable. Thank you!
The URL of the Json is here: https://cz2006api.herokuapp.com/api/getAll
Simply change your initial state to something like this
this.state = {
isLoading: true,
dataSource: [], // <-- here
};
Your problem is you're using dataSource.map but during api calling your dataSource still stay null until it get its response, and null object have no attribute map. That's the cause of your problem.
remove the return in componentDidMount:
componentDidMount() {
fetch("https://cz2006api.herokuapp.com/api/getAll")
.then((response) => response.json())
.then((responseJson) => {
this.setState({
dataSource: responseJson.data.data,
isLoading: false,
});
})
.catch((error) => {
console.log(error);
});
}
I agree with #Nguyễn's suggestion that your initial state should be an array. However the root of the issue seems to be getting the right properties off off your JSON response.
First, you want responseJson.data instead of responseJson.data.data. That gives me an array and shows a long list but the titles are all blank. That's because your response has Name as an uppercase property but you are accessing name. So you need to change that as well.
export default class ClinicComponent extends Component {
constructor(props) {
super(props);
this.state = {
isLoading: true,
dataSource: [],
};
}
componentDidMount() {
return fetch('https://cz2006api.herokuapp.com/api/getAll')
.then((response) => response.json())
.then((responseJson) => {
this.setState({
isLoading: false,
dataSource: responseJson.data,
});
})
.catch((error) => {
console.log(error);
});
}
render() {
//console.log(this.state.dataSource?.[0]);
if (this.state.isLoading) {
return (
<View style={styles.container}>
<ActivityIndicator />
</View>
);
} else {
return (
<View style={styles.item}>
{/* <Text>Content Loaded</Text> */}
{this.state.dataSource.map((val, key) => (
<View key={val._id} style={styles.item}>
<Text>{val.Name}</Text>
</View>
))}
</View>
);
}
}
}
You are fetching a huge amount of data and you probably want some sort of pagination with infinite scrolling. It is extremely slow to load due to the huge payload that we are fetching.
You also have double-escape problem in the JSON response inside the geocodingData section. You want to return this data as an object but it is an escaped string with lots of \" instead.

Parsing JSON with react native, looping through

i am trying to parse a json file by displaying all the names in the clubs
the json file is https://raw.githubusercontent.com/openfootball/football.json/master/2017-18/it.1.clubs.json
my current code i have is
constructor(props) {
super(props);
this.state = {
isLoading: true,
dataSource: null,
}
}
componentDidMount() {
return fetch('https://raw.githubusercontent.com/openfootball/football.json/master/2017-18/it.1.clubs.json')
.then ( (response) => response.json() )
.then ( (responseJson) => {
this.setState({
isLoading: false,
dataSource: responseJson.clubs,
})
})
.catch((error) => {
console.log(error)
});
}
render() {
if (this.state.isLoading) {
return (
<View style = {styles.containter}>
<ActivityIndicator/>
</View>
)
} else {
return (
<View>
<Text>{this.state.dataSource.name}</Text>
</View>
)
I just want to loop through to display all the names in the clubs
Try this:
Couple of edits: Changing the initial state of dataSouce value as an array, this is to ensure it doesn't throw can't read property map of undefined.
You don't need to return the fetch call, because you don't need a promise to returned.
EDIT: Added a loading text before all the clubs are loaded.
import React from "react";
import ReactDOM from "react-dom";
import "./styles.css";
class App extends React.Component {
state = {
isLoading: false,
dataSource: []
};
componentDidMount() {
this.setState({ isLoading: true }, () => {
fetch(
"https://raw.githubusercontent.com/openfootball/football.json/master/2017-18/it.1.clubs.json"
)
.then(response => response.json())
.then(responseJson => {
console.log(responseJson);
this.setState({
isLoading: false,
dataSource: responseJson.clubs
});
})
.catch(error => {
this.setState({ loading: false });
console.log(error);
});
});
}
render() {
return (
<div className="App">
<h1>Club Names</h1>
{this.state.isLoading ? (
<h1>Loading Clubs...</h1>
) : (
this.state.dataSource.map(data => <h2 key={data.key}>{data.name}</h2>)
)}
</div>
);
}
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
Sandbox Link: https://codesandbox.io/s/throbbing-dream-7lpcm?fontsize=14

How to fetch data from URL and then store it in AsyncStorage in React Native?

I'm trying to fetch data from URL and then store it in AsyncStorage in React Native.
This is screen where "magic" happens:
class CardsScreen extends Component {
constructor(props) {
super(props);
this.state = {
isLoading: true,
dataSource: null,
}
}
componentDidMount() {
return fetch(REQUEST_URL)
.then((response) => response.json())
.then((responseData) => {
this.setState({
isLoading: false,
dataSource: responseData,
});
})
.catch((error) => {
console.log(error)
});
}
render() {
if (netStatus) {
if (this.state.isLoading) {
return (
<View style={styles.container}>
<ActivityIndicator/>
</View>
)
} else {
let data = this.state.dataSource.map((val, key) => {
return <View key={key} style={styles.itemWrapper}>
<View style={styles.titleWrapper}>
<Text style={styles.content}>{val.type}</Text>
<Text style={styles.title}>{val.title}</Text>
<Text style={styles.content}>{val.date}</Text>
</View>
<View style={styles.imageWrapper}>
<Image
style={styles.image}
source={{uri: val.image}}
/>
</View>
<View style={styles.contentWrapper}>
<Text style={styles.content}>{val.adress}</Text>
<Text style={styles.content}>{val.text}</Text>
</View>
</View>
})
return (
<ScrollView contentContainerStyle={styles.containerScroll}>
{data}
</ScrollView>
);
}
} else {
return <View style={styles.contentWrapper}>
<Text style={styles.content}>Not connected!</Text>
</View>
}
}
};
This code prints data if device is connected to internet.
What I need to do is if device is conneted to internet, fetch data from URL and store (or overwrite) it in AsyncStorage, and then print data on screen. If device is not connect to the internet just print data from AsyncStorage.
This is example of .json I call from URL:
[
{
"type": "Party",
"title": "New party comming to town!",
"adress": "New Yrok",
"date": "20. 4. 2019.",
"image": "https:\/\/some-url.com\/some-image.jpg",
"text": [
"Some description"
],
"_id": "events_1"
}
]
I can't find any similar solution, so I would be greatfull if someone has tutorial that could help me with this.
EDIT (little more of explenation):
This is what I want to do:
If device is connected to the internet, update AsyncStorage with data from URL and then display that new data in AsyncStorage. If device is not connected to the internet, just display data that is AsyncStorage. And there are multiple "events", not just one like in the example.
export default class BankDetails extends Component {
constructor(props) {
super(props)
this.state = {
userToken: null,
userData: {
nameOfBank: '',
account_umber: '',
account_holder_ame: ''
}
}
}
async componentDidMount () {
try {
let value = await AsyncStorage.getItem('userData')
let userDetails = JSON.parse(value)
if (userDetails !== null) {
let userData= Object.assign({}, this.state.userData)
userData.nameOfBank = userDetails.nameOfBank
userData.account_umber = userDetails.account_umber
userData.account_holder_ame = userDetails.account_holder_ame
this.setState({
userToken: userDetails.token,
userData
})
}
} catch (error) {
console.log(error)
}
}
onBankChange = (nameOfBank) => {
this.setState({userData: {...this.state.userData, nameOfBank:nameOfBank}})
}
saveBankDetailsEdit = () => {
const { nameOfBank,account_umber, account_holder_ame }= this.state.userData
let {userToken} = this.state
var formData = new FormData()
formData.append('bank', nameOfBank)
formData.append('account_number', account_umber)
formData.append('account_title', account_holder_ame)
fetch('http://xxxx/update', {
method: 'POST',
headers: {
'X-API-KEY': '123456',
'Authorization': userToken
},
body: formData
}).then(function (response) {
console.log(response)
AsyncStorage.getItem('userData').then(
data => {
data = JSON.parse(data)
data.nameOfBank = nameOfBank
data.account_umber = account_umber
data.account_holder_ame = account_holder_ame
AsyncStorage.setItem('userData', JSON.stringify(data))
}
)
let data = JSON.parse(response._bodyText)
Alert.alert(data.message)
})
.catch((error) => {
console.log(error)
})
.done()
}
This is how I have done my job. Hope this helps.
You need to store your response like this
note one thing when you set value into AsyncStorage you need to store as string JSON.stringify() value into storage.
onSave = async () => {
try {
await AsyncStorage.setItem("key", JSON.stringify(responseData));
Alert.alert('Saved', 'Successful');
} catch (error) {
Alert.alert('Error', 'There was an error.')
}
}
and when you get value from AsyncStorage you need to JSON.parse()
onSave = async () => {
try {
const storedValue = await AsyncStorage.getItem("key");
this.setState({ storedValue: JSON.parse(storedValue) });
} catch (error) {
Alert.alert('Error', 'There was an error.')
}
}
hope it will help you

React native dynamically pop and show map markers from server

I need to get marker positions on map position change from the server and reflect this new marker positions on the map. If I put some markers in the state it shows the markers however it doesn't show any marker dynamically. I don't get any error or warnings on the console.
App.js
import React, { Component } from 'react';
import { Text, View, StyleSheet, Switch, Alert, AppRegistry } from 'react-native';
import MapView from 'react-native-maps';
import Fetchdata from './Fetchdata.js';
const styles = StyleSheet.create({
container: {
...StyleSheet.absoluteFillObject,
height: 400,
width: 400,
justifyContent: 'flex-end',
alignItems: 'center',
},
map: {
...StyleSheet.absoluteFillObject,
},
});
export default class MyScreen extends Component {
render() {
return (
<View style ={styles.container}>
<Fetchdata />
</View>
);
}
}
FetchData.js
import React, { Component } from 'react'
import { Text, View, StyleSheet, Switch, Alert, AppRegistry} from 'react-native'
import MapView, {Marker} from 'react-native-maps';
const styles = StyleSheet.create({
container: {
...StyleSheet.absoluteFillObject,
height: 400,
width: 400,
justifyContent: 'flex-end',
alignItems: 'center',
},
map: {
...StyleSheet.absoluteFillObject,
},
});
export default class Fetchdata extends Component {
constructor (props) {
super(props);
};
state = {
latitude: 40.3565,
longitude: 27.9774,
markers:[]
};
componentDidMount = () => {
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 },
);
}
onRegionChange (region) {
return fetch('https://isg.info.tr/query_maps.php' + '?latitude=' + this.state.latitude + '&longitude=' + this.state.longitude , {method: 'GET'})
.then((response) => response.json())
.then((responseJson) => {
const newState = Object.assign({}, this.state);
newState.markers.latlng = responseJson;
newState.latitude = region.latitude;
newState.longitude = region.longitude;
this.setState(newState);
console.log(responseJson);
})
};
render() {
return (
<View style={styles.container}>
<MapView
style={styles.map}
region={{
latitude: this.state.latitude,
longitude: this.state.longitude,
latitudeDelta: 0.015,
longitudeDelta: 0.015,
}}
onRegionChangeComplete={this.onRegionChange.bind(this)}
>
{this.state.markers.map(marker => (
<Marker
coordinate={marker.latlng}
title={"marker.title"}
description={"marker.description"}
/>
))}
</MapView>
</View>
);
}
}
JSON File that is returned by the server
[{"latitude":"40.3565","longitude":"27.9774"},{"latitude":"40.3471","longitude":"27.9598"},{"latitude":"40","longitude":"27.9708"}]
There are a few things going on there that are a problem and should be giving you an error. The problem is that none of those are being triggered because you're using onRegionChangeComplete, but the correct property is just onRegionChange. The errors at that point may get you the rest of the way.
If you get to a point where you've gone through the errors and are still not sure where to go, feel free to ask for more clarification.