React native: Undefined is not an object - reactjs

I'm making a react native app with expo and I'm having some trouble parsing the response data from the api. Below is a sample response from the api and my goal is to retrieve main from the weather array. I can log weather array just fine but when I put state.weather[0].main it throws the undefined object error.
Object {
"clouds": Object {
"all": 20,
},
...,
"weather": Array [
Object {
"description": "few clouds",
"icon": "02d",
"id": 801,
"main": "Clouds",
},
],
}
Below is my code, I fetch from the api on useEffect() hook. At first the logs shows undefined but after fetching it logs correctly except for state.weather[0].main. I just started react native coming from Android, I hope you guys could help me, I'm stuck for a day now. Thanks
const HomeScreen = ({ navigation }) => {
const { state: { defaultCity } } = useContext(CitiesContext);
const { state: { weather }, fetchWeather } = useContext(WeatherContext);
useEffect(() => {
if (defaultCity) {
fetchWeather(defaultCity);
}
}, []);
console.log(weather);
console.log(typeof (weather[0]));
console.log(weather[0]);
console.log(weather[0].main);
return (
<View style={styles.container}>
<LinearGradient
// {...weatherBgProvider[weather.weather.main]}
colors={['#000', '#fff']}
style={styles.gradient}
>
<SafeAreaView
style={{
position: 'relative'
}}
forceInset={{ top: 'always' }}
>
<View style={{ marginTop: Header.HEIGHT, flex: 1 }}>
<Text >{JSON.stringify(state)}</Text>
</View>
</SafeAreaView>
</LinearGradient>
</View>
);
};

If you are trying to log data in your render, you can do it like this:
<View>{This.state.weather[0] && console.log(this.state.weather[0]) }</View>
So you check it to exist before you log data
Hope you get the idea and works for you
update
If your main not working, first try to parse your object into array like this:
first way use lodash (npm i --dev lodash) and import it in your project
Import _ from "lodash";
Const newdata= _.compact(Object.values(yourobject))
And then you can use newdata and i think your problem will be solved
second way use _.mapKeys(yourdata,"id") and put new data in your variable like const newdata
I hope it works

Related

How should I display single-data-object fetched by API in react-native?

I am learning React-Native and trying to display user profile on profile screen.
Displaying a list of objects obtained from the server is pretty straightforward, but I am confused on how do I display a single-object that my API-call fetches.
This is my fetched object data output which I get when I do console.log(getProfileApi.data) right before displaying it.
Object {
"email": "user1#gmail.com",
"first_name": "User",
"gender": "Male",
"id": 2,
"last_name": "1",
"profile": Object {
"address": null,
"city": null,
"country": null,
"dob": "2021-11-01",
"profile_pic": "http://192.168.0.218:8000/media/profile/user1_profile.jpg",
"title": null,
"zip": null,
},
"user_type": "Owner",
}
This is how I am trying to display the fetched data, but the data is not displayed.
<FlatList
// getProfileApi contains the response from server which is shown above
data={getProfileApi.data}
renderItem={({ item }) => (
<Text>{item.email}</Text>
)}
/>
How do I extract the data and display it. Is there any other component to use instead of Flat-List to display a single-object data?
First of all, FlatList is used to display the data of the array. It is usually used to display big lists, like products list in an e-commerce app, users lists, posts, etc. To display simple data as you have, you can use ScrollView. Follow the below approach to display your data in ScrollView.
import React, { useCallback, useEffect, useState } from 'react';
import { View, Text, ScrollView, StyleSheet } from 'react-native';
const YourScreenName = () => {
// declare state variable to handle loading UI
// set initial value to true, so when screen load initially it display loading
const [isLoading, setLoading] = useState(true);
const [user, setUser] = useState(null);
useEffect(() => {
getData();
}, []);
const getData = useCallback(() => {
setLoading(true); // Start the loader, So when you start fetching data, you can display loading UI
getProfileApi().then((data) => {
setUser(data);
setLoading(false);
}).catch((error) => {
// display error
setLoading(false); // stop the loader
})
// or your any data fetching query
// setUser(getProfileApi.data);
// setLoading(false);
}, []);
return (
<ScrollView
style={styles.container}
contentContainerStyle={styles.contentContainer}
>
{
isLoading && (
<Text>Loading</Text>
);
}
{
!isLoading && user && (
<View>
<Text>Email : {user.email}</Text>
<Text>Name : {user.name}</Text>
</View>
)
}
</ScrollView>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
},
contentContainer: {
flexGrow: 1,
justifyContent: 'center',
alignItems: 'center'
}
});
export default YourScreenName;
Instead of using a flat list you can directly render the UI component and make it's content dynamic just like below
<Text>{getProfileApi.data.email}</Text>
there is no need for a flat list here because it is used to render a list of data but for a single data set, you can directly use it in your UI.
First create a state like this
const [myArray, setMyArray] = useState([]);
then you have to set your array in the state after you have fetched the data from your api instead of directly calling getProfileApi which looks like a function
setMyArray(yourresponsearray);
then inside your Flatlist component you can pass that state inside the data prop
<FlatList
data={myArray}
renderItem={({ item }) => (
<Text>{item.email}</Text>
)}
/>

Using Hooks API: does React respect setState order?

I have fairly nonexistent knowledge in react but I'm learning as I go. I learned the basics back in school, with class components (classic React), but now I'm delving into the Hooks API (mainly because I find it easier to learn and manage, although there seems to be more tricks involved regarding async behavior). So my question might seem silly.
I found this thread regarding setState behavior on the same topic, but this is regarding class components.
In my current application, I'm trying to set three different states using an event handler. It seems that the last state is set immediately, whereas the other two states remain undefined for a bit before changing to a real value. I'm using React-Native components for mobile development, so you'll see snippets in the code such as <SafeAreaView>.
export default App = () => {
const [ destLong, setDestLong ] = useState();
const [ destLat, setDestLat ] = useState();
const [ startNav, setStartNav ] = useState(false);
const [ locations, setLocations ] = useState([
{
name: 'IKEA',
long: '-74.00653395444186',
lat: '40.68324646680103',
},
{
name: 'JFK Intl. Airport',
long: '-73.78131423688552',
lat: '40.66710279890186',
},
{
name: 'Microcenter',
long: '-74.00516039699959',
lat: '40.67195933297655',
}
]);
const startNavigation = (goinglong, goinglat) => {
setDestLong(goinglong);
setDestLat(goinglat);
setStartNav(true);
}
return (
<SafeAreaView style={styles.container}>
{ startNav ?
<MapView
destLong = {destLong}
destLat = {destLat}
/>
:
<View style={styles.buttonContainer}>
<ScrollView>
{
locations.map((location, i) => {
return(
<Card
style={styles.card}
key={i}
title={ location.name }
iconName="home"
iconType="Entypo"
description={ location.long + ", " + location.lat }
onPress={() => startNavigation(location.long, location.lat)}
/>
);
})
}
</ScrollView>
</View>
}
</SafeAreaView>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
},
buttonContainer: {
width: '100%',
height: '100%',
justifyContent: 'center',
alignItems: 'center'
},
logo: {
width: '50%',
height: '50%',
resizeMode: 'contain'
},
card: {
marginBottom: 10,
}
});
This throws an error, because MapView is expecting destLong and destLat to render properly. When I console log inside my startNavigation function, it seems that it immediately updates the state for startNav to true onPress, but destLong and destLat remain undefined for a few cycles before being set.
I've tried a different approach like this:
useEffect(() => {
setStartNav(true);
}, [destLong]);
const startNavigation = (goinglong, goinglat) => {
setDestLong(goinglong);
setDestLat(goinglat);
}
But it just crashes the app (my guess is infinite loop).
I've also tried removing the startNav state altogether and rendering <MapView> on destLong like this
{ destLong ?
<MapView
destLong = {destLong}
destLat = {destLat}
/>
:
<View style={styles.buttonContainer}>
...
</View>
}
But that did not work either.
Which brings me to this question: does the Hooks API respect the order of setState, or is each other carried out asynchronously? From my understanding it's the latter. But then, how do you handle this behavior?
I'm adding my comment here as well since I am unable to add proper formatting to my comment above.
Setting a state via useState is actually asynchronous, or rather the state change is enqueued and it will then return its new value after a re-render. This means that there is no guarantee in what order the states will be set. They will fire in order, but they may not be set in the same order.
You can read more here: https://dev.to/shareef/react-usestate-hook-is-asynchronous-1hia, as well as here https://blog.logrocket.com/a-guide-to-usestate-in-react-ecb9952e406c/#reacthooksupdatestate
In your case I would use useState and useEffect like this:
useEffect(() => {
if(destLong && destLat && !startNav) {
setStartNav(true);
}
}, [destLong, destLat, startNav]);
const startNavigation = (goinglong, goinglat) => {
setDestLong(goinglong);
setDestLat(goinglat);
}
With that said, I think you could further simplify your code by omitting the startNav state altogether and update your conditional render:
{ (destLat && destLong) ?
<MapView
destLong = {destLong}
destLat = {destLat}
/>
:
<View style={styles.buttonContainer}>
...
</View>
}
The above should have the same effect since you have two states that are undefined to begin with, and when they are both defined you want to render something and use their values.
And if you want to display the options again you can set the states to undefined again by doing setDestLat(undefined) and setDestLong(undefined)

React Native Filter by Category using Button or Touchableopacity

How to Filter the record based status on the button click in the react native ?
Here is the data I have:
[
{
"id":28,
"Title":"Sweden",
"Status":1
},
{
"id":56,
"Title":"USA",
"Status":1
},
{
"id":89,
"Title":"England"
"Status":1
},
{
"id":89,
"Title":"England"
"Status":2
},
{
"id":89,
"Title":"England"
"Status":2
},
{
"id":89,
"Title":"England"
"Status":3
}
]
Here I have id, title, status..Now I need to filter this according to the status
Critique
Although your question is very specific in terms of programming capabilities, it is very lacking in terms of context.
Keep in mind that people answering these questions do so in their own time, questions like yours, are sometimes really hard to cypher.
missing key elements in your question:
At what stage should the filtering be done?
Did you already try something that didn't work?
Do you already have a running react-native app? or are you starting from scratch?
Going with react-native, are you using hooks? or are extending the React.Component class?
Is the issue you are having even react-native related? or just JavaScript? because with a quick google search, you would've landed here: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/filter
I could go on, but the bottom line, help us, help you,by painting a picture around your current situation, it will help you get faster, better answers.
I hop I won't discourage from participating in the future, but understand that these kind of question really make it hard to understand, what you need.
Answer
Now, to try to answer what I think you need answering.
like #nithinpp already stated, filtering arrays in JavaScript can be done with the Array.prototype.filter() method, it usage usually looks like so:
// Example taken from the https://developer.mozilla.org/ website
const words = ['spray', 'limit', 'elite', 'exuberant', 'destruction', 'present'];
const result = words.filter(word => word.length > 6);
console.log(result);
// expected output: Array ["exuberant", "destruction", "present"]
Within The React/React-Native realms, this method would have to be used, either inside a react(-native) component, or inside a utility-service/reducers/saga that filters said data, and passes it on to the component through props.
For the sake of this example, I'll be using react-hooks, and do the filtering inside a component, such a solution could look like so:
Playable Online Version using Expo Snack: https://snack.expo.dev/yTte5nPud
//This is an example of online Emulator by https://aboutreact.com
import React, {useState, useMemo} from 'react';
import { Text, View, StyleSheet, Button, FlatList } from 'react-native';
// Item Component To render single items
function Item(props) {
return (
<View style={styles.item}>
<Text>{props.item.id}: {props.item.Title} ({props.item.Status})</Text>
</View>
);
}
// Main App
const App = () => {
// There are a lot of different ways to keep this info,
// for the sake of this answer, I've put it in a useState hook
const [fullList, setFullList] = useState([
{ "id":28, "Title":"Sweden", "Status":1 },
{ "id":56, "Title":"USA", "Status":1 },
{ "id":89, "Title":"England", "Status":1 },
{ "id":89, "Title":"England", "Status":2 },
{ "id":89, "Title":"England", "Status":2 },
{ "id":89, "Title":"England", "Status":3 }
]);
// Keep a statue of the current selected status
const [status, setStatus] = useState('NONE')
// the filtered list, cached with useMemo
// the callback is call each time the status or the fullList changes
const filteredList = useMemo(
() => {
if (status === 'NONE' ) return fullList
return fullList.filter(item => status === item.Status)
},
[status, fullList]
)
// the onClick Method is a method that returns a method, which
// updates the state based on the predefined status
const onClick = (status) => () => {
setStatus(status)
}
// render list using flat list, and the filter bar using standard buttons
return (
<View style={styles.container}>
<Text>Selected Status: {status}</Text>
<View style={styles.filterBar}>
<Button title="Clear" onPress={onClick('NONE')} />
<Button title="Status 1" onPress={onClick(1)} />
<Button title="Status 2" onPress={onClick(2)} />
<Button title="Status 3" onPress={onClick(3)} />
</View>
<FlatList
style={styles.list}
renderItem={Item}
keyExtractor={(item) => item.id}
data={filteredList}
/>
</View>
);
};
export default App;
// some basic styling
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
padding: 8,
backgroundColor: 'white',
},
list: {
height: '100%',
width: '100%'
},
filterBar: {
flexDirection: 'row',
// flex: 0.2,
height: 40,
},
item: {
flex: 1,
justifyContent: 'flex-start',
padding: 8,
backgroundColor: 'white',
}
});
Summary
Now This answer assumes a lot of things, I still don't know the extent of your knowledge regarding react-native, or if you even know how to use react-native components such as the FlatList, and this answer should not start explaining those as well, thus might end up confusing you and giving you an answer to something you haven't even asked, which I hope is not the case.
Anyways, I hope this helps, and maybe keep these points in mind in your next question
You can use Array Filter to filter your data based on your condition.
For example,
function filterArray(array, status){
return array.filter(item => item.Status === status);
}
const DATA = [ { "id":28, "Title":"Sweden", "Status":1 }, { "id":56, "Title":"USA", "Status":1 }, { "id":89, "Title":"England", "Status":1 }, { "id":89, "Title":"England", "Status":2 }, { "id":89, "Title":"England", "Status":2 }, { "id":89, "Title":"England", "Status":3 } ];
function filterArray(array,status){
return array.filter(item => item.Status === status);
}
const statusOneArray = filterArray(DATA, 1);
console.log(statusOneArray);

react-native-background-timer, null is not an object

I've got an error using react-native-background-timer. I would be appreciate it if you could help me solve this problem.
I'm developing a mobile app on Expo Snack, and I now want to realize the auto-delete-account function: when an account is created and not being verified for 5 minutes, it will be deleted automatically.
So, I searched about background timer and I found the library below.
https://github.com/ocetnik/react-native-background-timer
However, I wasn't able to achieve it because of the error below
(3:2693) null is not an object (evaluating 'o.setTimeout')
and this is my code
import React, { Component } from 'react';
import { View, Text, TouchableOpacity, Platform } from 'react-native';
import BackgroundTimer from 'react-native-background-timer';
let counter = 0;
let timer = null;
export default class App extends Component {
constructor(props) {
super(props);
this.state = {
second: 0,
};
}
_interval: any;
onStart = () => {
if (Platform.OS == 'ios') {
BackgroundTimer.start();
}
this._interval = BackgroundTimer.setInterval(() => {
this.setState({
second: this.state.second + 1,
});
}, 1000);
};
onPause = () => {
BackgroundTimer.clearInterval(this._interval);
};
onReset = () => {
this.setState({
second: 0,
});
BackgroundTimer.clearInterval(this._interval);
};
render() {
return (
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
<TouchableOpacity onPress={this.onStart}>
<Text>start</Text>
</TouchableOpacity>
<TouchableOpacity onPress={this.onPause}>
<Text>pause</Text>
</TouchableOpacity>
<TouchableOpacity onPress={this.onReset}>
<Text>reset</Text>
</TouchableOpacity>
<Text>{this.state.second}</Text>
</View>
);
}
}
I followed a tutorial of this guy
https://medium.com/#loons.create/how-to-setup-react-native-background-timer-22263d655847
The equipped function, setInterval of javascript and etc. of course works fine as a timer, but actually they don't work behind in react native.
What am I missing, or is this an issue inside this library( I suppose so )? If so, please tell me an available version of this library; I use the latest version, 2.2.0, and React v35.0.0
Thank you
You cannot use "react-native-background-timer" with Expo on managed workflow. This library needs to compile some native code.
Instead, you should take a took to Expo BackgroundFetch which is doing almost the same thing.
https://docs.expo.io/versions/latest/sdk/background-fetch/
Using Expo components, you don't need to eject or compile additional native code.

I am working on a project and I need to translate speech into text I used Expo and React-native-voice

I am working on a project and I need to translate speech into text I used Expo and React-native-voice.
**[Unhandled promise rejection: TypeError: null is not an object (evaluating 'Voice.onSpeechStart = null')]
- node_modules\react-native-voice\src\index.js:23:10 in removeAllListeners
- node_modules\promise\setimmediate\core.js:37:14 in tryCallOne
- ... 9 more stack frames
import React from "react";
import { StyleSheet, Text, View, TouchableOpacity } from "react-native";
import Voice from "react-native-voice";
import * as Permissions from "expo-permissions";
export default class App extends React.Component {
constructor() {
super();
this.state = {
results: [],
};
Voice.onSpeechStart = this.onSpeechStart;
Voice.onSpeechRecognized = this.onSpeechRecognized;
Voice.onSpeechEnd = this.onSpeechEnd;
Voice.onSpeechError = this.onSpeechError;
Voice.onSpeechResults = this.onSpeechResults;
Voice.onSpeechPartialResults = this.onSpeechPartialResults;
Voice.onSpeechVolumeChanged = this.onSpeechVolumeChanged;
}
async componentDidMount() {
const {status} = await Permissions.askAsync(
Permissions.AUDIO_RECORDING
);
}
componentWillMount(){
Voice.destroy().then(Voice.removeAllListeners)
}
onSpeechStart = (e)=> {
console.log('onSpeechStart',e);
};
onSpeechRecognized =(e)=>{
console.log('onSpeechRecognized',e);
}
onSpeechEnd = (e)=>{
console.log('onSpeechEnd'+e);
}
onSpeechError =(e)=>{
console.log('onSpeechError');
}
onSpeechResults = e => {
console.log('onSpeechResults'+e);
this.setState({
results: e.value,
});
}
onSpeechPartialResults = e =>{
console.log('onSpeechPartialResults' + e.value);
}
onSpeechVolumeChanged = e =>{
console.log('onSpeechVolumeChanged');
}
_startRecognizing=async()=>{
try{
await Voice.start('en-US')
} catch(e) {
console.error(e);
}
}
_stopRecognizing = async()=>{
try{
await Voice.stop()
}catch(e){
console.error(e);
}
}
render() {
return (
<View style={styles.container}>
<TouchableOpacity
onPress={this._startRecognizing}
style={{
backgroundColor: "green",
height: 40,
width: 100,
marginBottom: 10,
}}
>
<Text style={{
fontSize: 20,
alignSelf: "center"
}}>
Starts
</Text>
</TouchableOpacity>
<TouchableOpacity
onPress={this._stopRecognizing}
style={{
backgroundColor: "red",
height: 40,
width: 100
}}
>
<Text style={{
fontSize: 20,
alignSelf: "center"
}}>
Stop
</Text>
</TouchableOpacity>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: "#fff",
alignItems: "center",
justifyContent: "center",
},
});
from framework internals**
Update for Expo 41+
With the release of the Expo SDK 41, config plugins were added. The 3.2.0 release of react-native-voice/voice added support for config plugins.
You install the dependency as you normally would
yarn add #react-native-voice/voice
And then you must update your app.json
"expo": {
"plugins": [
[
"#react-native-voice/voice",
{
"microphonePermission": "Allow $(PRODUCT_NAME) to access your microphone",
"speechRecogntionPermission": "Allow $(PRODUCT_NAME) to securely recognize user speech"
}
]
]
}
You will probably need to create a new build as you may get an invariant violation.
You should now read the documentation for usage
—-
Expo 40 and older
Unfortunately react-native-voice is not compatible with Expo.
The reason for this is that Expo abstracts the native iOS and Android code away from you, this makes it quick and easy to make builds and develop but the downside is that you cannot add dependencies that require you to use native code.
react-native-voice requires the use of native code. You can see this by the fact that the installation instructions require you to link the native code. See here for the documentation.
If you wish to use this dependency in your Expo project then you will need to eject it. You can find more info about ejecting, and the pros and cons of doing so on the Expo documentation website here
With Expo SDK42 you can use the react-native-voice plugin, here is what they have in the docs here
I have the same issue developing an react native app with react-native-voice package. I followed every step of the workflow (like instructed in the react-native-voice documentation as well as suggested by other answers), there seems nothing wrong -
expo run:android build successful. Then expo start --dev-client on my android emulator/physical device both throw this error:
[Unhandled promise rejection: TypeError: null is not an object
(evaluating 'Voice.isSpeechAvailable')]
In the end, I found that, one needs to open two terminals in vscode, one terminal running exp start (while connecting to your device), and the other terminal simultaneously runs expo run:android.
The app then is running on the device like a charm!
Hope this helps, and hope someone can explain more on this regarding how react-native/expo test development works?

Resources