undefined is not a function (near '....data.map....') |React native - reactjs

I am new to React native and trying to destructure the json response which i have got from my API call using the map function and somehow it is giving me the above error. I want to display the aqi and dominant pollutants using a text componenet. I am using the AQICN API.
import React,{ useState , useEffect} from 'react';
import { StyleSheet, Text, View ,ActivityIndicator, ScrollView,FlatList} from 'react-native';
import * as Location from 'expo-location';
export default function HomeScreen({navigation}) {
const [location, setLocation] = useState(null);
const [errorMsg, setErrorMsg] = useState(null);
//Lat and Long
const [latitude, setLatitude] = useState(null);
const [longitude , setLongitude]= useState(null);
const [data, setData] = useState([]);
const [loader, setLoader]=useState(true);
useEffect(() => {
(
async () => {
let { status } = await Location.requestPermissionsAsync();
if (status !== 'granted') {
setErrorMsg('Permission Denied');
return;
}
let location = await Location.getCurrentPositionAsync({});
setLocation(location);
//Changes
setLatitude(location.coords.latitude);
setLongitude(location.coords.longitude);
const la=latitude;
const lo=longitude;
async function AqicnApiCall() {
let res = await fetch("https://api.waqi.info/feed/geo:"+ latitude +";"+ longitude +"/?token=ac3a71fc80931abd95ede14c2040f0678f578703")
.then((response) => response.json())
.then((json) => setData(json.data))
.catch((error) =>console.log(error))
}
AqicnApiCall();
})();
}, [latitude, longitude]);
//const obj=JSON.stringify(data);
return (
<ScrollView style={styles.container}>
{
data.map((d) =>{
console.log(d);
return(
<Text style={styles.container}>{d.data.aqi}</Text>
)
})
}
</ScrollView>
);
}
const styles= StyleSheet.create({
container: {
padding:20,
marginTop:15,
margin:10,
},
paragraph : {
padding:20,
marginTop:5,
}
});
This is the API response, i need the dominant pollutant and aqi.

Working App: Expo Snack
Access aqi and dominentpol like below:
return (
<ScrollView style={styles.container}>
<Text style={styles.container}>AQI: {data?.aqi}</Text>
<Text style={styles.container}>
Dominant Pollutant: {data?.dominentpol}
</Text>
</ScrollView>
);
}
Full Working code:
import React, { useState, useEffect } from 'react';
import {
StyleSheet,
Text,
View,
ActivityIndicator,
ScrollView,
FlatList,
} from 'react-native';
import * as Location from 'expo-location';
export default function HomeScreen({ navigation }) {
const [location, setLocation] = useState(null);
const [errorMsg, setErrorMsg] = useState(null);
//Lat and Long
const [latitude, setLatitude] = useState(null);
const [longitude, setLongitude] = useState(null);
const [data, setData] = useState([]);
const [loader, setLoader] = useState(true);
useEffect(() => {
(async () => {
let { status } = await Location.requestPermissionsAsync();
if (status !== 'granted') {
setErrorMsg('Permission Denied');
return;
}
let location = await Location.getCurrentPositionAsync({});
setLocation(location);
//Changes
setLatitude(location.coords.latitude);
setLongitude(location.coords.longitude);
const la = latitude;
const lo = longitude;
async function AqicnApiCall() {
let res = await fetch(
'https://api.waqi.info/feed/geo:' +
latitude +
';' +
longitude +
'/?token=ac3a71fc80931abd95ede14c2040f0678f578703'
)
.then((response) => response.json())
.then((json) => {
console.log('data: ', json.data);
setData(json.data);
})
.catch((error) => console.log(error));
}
AqicnApiCall();
})();
}, [latitude, longitude]);
//const obj=JSON.stringify(data);
return (
<ScrollView style={styles.container}>
<Text style={styles.container}>AQI: {data?.aqi}</Text>
<Text style={styles.container}>
Dominant Pollutant: {data?.dominentpol}
</Text>
</ScrollView>
);
}
const styles = StyleSheet.create({
container: {
padding: 20,
marginTop: 15,
margin: 10,
},
paragraph: {
padding: 20,
marginTop: 5,
},
});

As data isn't an array, and I see that you just want to display aqi value,
<ScrollView style={styles.container}>
<Text style={styles.container}>{data?.aqi}</Text>
</ScrollView>
On the screenshot, it seems that you can only iterate on data.attributions.
<ScrollView style={styles.container}>
{data.attributions.map(attr => <Text>{attr.something}</Text>}
</ScrollView>

I think some of d.data is undefined or null. Please try this.
<ScrollView style={styles.container}>
{
data && data.map((d) =>{
console.log(d);
return(
<Text style={styles.container}>{d?.data?.aqi}</Text>
)
})
}
</ScrollView>

Related

TypeError: undefined is not an object (evaluating '_app.firebase.apps')

Can someone help with this typeerror. I am trying to connect to firebase through an expo go app and this error will come up at every place I have a firebase. I was guessing that the issue was with the imports as the initial import was outdated for firebase. If I am not doing the import correctly I would love to know.
My code is as follows:
// #refresh reset
import React , {useState, useEffect, useCallback} from 'react';
import AsyncStorage from '#react-native-async-storage/async-storage'
import { StyleSheet, Text, TextInput, Button, View } from 'react-native';
import { initializeApp, firebase } from "firebase/app";
import { getFirestore, collection, getDocs } from 'firebase/firestore/lite';
import { GiftedChat } from 'react-native-gifted-chat'
const firebaseConfig = {
//info for database
};
if (firebase.apps.length === 0){ //THIS IS WHERE THE ERROR KEEPS COMING UP FIRST
firebase.initializeApp(firebaseConfig)
}
const db = firebase.firestore()
const chatsRef = db.collection('chats')
export default function App() {
const [user, setUser] = useState(null)
const [name, setName] = useState('')
const [messages, setMessages] = useState([])
useEffect(() => {
readUser()
const unsubscribe = chatsRef.onSnapshot((querySnapshot) => {
const messagesFirestore = querySnapshot
.docChanges()
.filter(({ type }) => type === 'added')
.map(({ doc }) => {
const message = doc.data()
//createdAt is firebase.firestore.Timestamp instance
//https://firebase.google.com/docs/reference/js/firebase.firestore.Timestamp
return { ...message, createdAt: message.createdAt.toDate() }
})
.sort((a, b) => b.createdAt.getTime() - a.createdAt.getTime())
appendMessages(messagesFirestore)
})
return () => unsubscribe()
}, [])
const appendMessages = useCallback(
(messages) => {
setMessages((previousMessages) => GiftedChat.append(previousMessages, messages))
},
[messages]
)
async function readUser() {
const user = await AsyncStorage.getItem('user')
if (user) {
setUser(JSON.parse(user))
}
}
async function handlePress() {
const _id = Math.random().toString(36).substring(7)
const user = { _id, name }
await AsyncStorage.setItem('user', JSON.stringify(user))
setUser(user)
}
async function handleSend(messages) {
const writes = messages.map((m) => chatsRef.add(m))
await Promise.all(writes)
}
if (!user) {
return (
<View style={styles.container}>
<TextInput style={styles.input} placeholder="Enter your name" value={name} onChangeText={setName} />
<Button onPress={handlePress} title="Enter the chat" />
</View>
)
}
return <GiftedChat messages={messages} user={user} onSend={handleSend} />
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
alignItems: 'center',
justifyContent: 'center',
padding: 30,
},
input: {
height: 50,
width: '100%',
borderWidth: 1,
padding: 15,
marginBottom: 20,
borderColor: 'gray',
},
})
Dont use firebase for web apps for react-native apps. It will work for the easiest things but is not optimized for it and you will have a lot of trouble with it when further developing your app. I did it too and it didnt work out eventually.
Use react-native-firebase. That will work without any problems and doesnt take long to set up

Search is not being updated

I am using the meal database.The data from the link is not being updated after search. But if I console.log the search input, I can see the new link.
That's my API for searching:
API_URL_SEARCH="https://www.themealdb.com/api/json/v1/1/search.php?s="
Thats search page:
function Meals({ navigation}) {
const [searchInput, setSearchInput] = useState('');
const handleChange = (inputText) => {
setSearchInput(inputText);
};
const { loading, error, data } = useFetch(config.API_URL_SEARCH + searchInput);
const handleMealSelect = idMeal => {
navigation.navigate("MealDetail", {idMeal})
}
const renderMeals = ({item}) => <Meal meal={item} onSelect={() => handleMealSelect(item.idMeal)}/>
if(loading) {
return <Loading/>;
}
if(error) {
return <Error/>;
}
return(
<View>
<SearchBar
placeholder="Type Here..."
onChangeText={handleChange}
value={searchInput} />
<FlatList keyExtractor={(meals) => meals.id} data={data.meals} renderItem={renderMeals}/>
</View>
)
}
Thats meal component:
const Meal= ({meal, onSelect}) => {
return(
<TouchableOpacity style={styles.container} onPress={onSelect}>
<ImageBackground
style={styles.image}
source={{uri: meal.strMealThumb}}
imageStyle={{borderTopLeftRadius:10, borderTopRightRadius:10}} />
<Text style={styles.title}>{meal.strMeal}</Text>
</TouchableOpacity>
)
}
Here is useFetch for getting data and getting loading and error situations just in case of.
function useFetch(url) {
const [data, setData] = useState([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState();
const fetchData = async () => {
try {
const {data: responseData} = await axios.get(url);
setData(responseData);
setLoading(false); }
catch (error) {
setError(error.message);
setLoading(false);
}
};
useEffect(() => {
fetchData();
}, []);
return {error, loading, data};
};

Cannot get datas on json with flatlist

I am using the Free Meal API with flatlist. I have Category component, Categories page, useFetch hook. I can't see Flatlist on screen. I can get console log of data but I can't reach datas with flatlist.
.env folder:
API_URL_CATEGORIES="https://www.themealdb.com/api/json/v1/1/categories.php"
API_URL_FILTER="https://www.themealdb.com/api/json/v1/1/filter.php?"
useFetch hook for getting the data in URL and returning Loading icon, Error if URL doesn't work and data for data in URL.
function useFetch(url) {
const [data, setData] = useState([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState();
const fetchData = async () => {
try {
const {data: responseData} = await axios.get(url);
setData(responseData);
setLoading(false); }
catch (error) {
setError(error.message);
setLoading(false);
}
};
useEffect(() => {
fetchData();
}, []);
return {error, loading, data};
};
export default useFetch;
Category component:
const Category= ({category, onSelect}) => {
return(
<TouchableOpacity style={styles.container} onPress={onSelect}>
<Image
style={styles.image}
source={{uri:category.strCategoryThumb}} />
<Text style={styles.title}>{category.strCategory}</Text>
</TouchableOpacity>
)
}
export default Category;
Categories page:
const Categories = ({navigation}) => {
const { error, loading, data } = useFetch(config.API_URL_CATEGORIES);
console.log(data)
const handleCategorySelect = strCategory => {
navigation.navigate("Detail", {strCategory})
}
const renderCategory = ({item}) => <Category category={item} onSelect={() => handleCategorySelect(item.strCategory)}/>;
if(loading) {
return <Loading/>;
}
if(error) {
return <Error/>;
}
return(
<View style={styles.container}>
<FlatList data={data} renderItem={renderCategory}/>
<Text>Categorises</Text>
</View>
)
}
export default Categories;
I think data is actually object that contains a property categories , which holds an array.
try data.categories and I believe this should work fine.

.map function not working with React Native

I have a .map() function with JSX code inside. Although, the JSX is not rendering. It is only rendering after I save the file. I am using expo (React Native).
Here is my code:
import React, { useEffect, useState } from "react";
import * as SecureStore from "expo-secure-store";
import { View, Text, ActivityIndicator } from "react-native";
import { Button } from "react-native-elements";
const Receipts = ({ navigation }) => {
const [receipts, setReceipts] = useState([]);
const [loading, setLoding] = useState(true);
const [result, setResult] = useState({});
const [keys, setKeys] = useState([]);
useEffect(() => {
const getReceiptsData = async () => {
let token = await SecureStore.getItemAsync("token");
console.log(token);
fetch("https://notrealapi/api/receipts", {
method: "GET",
headers: {
Authorization: `JWT ${JSON.parse(token)}`,
},
})
.then((res) => res.json())
.then((json) => {
setReceipts(json);
setLoding(false);
})
.catch((error) => console.error(error));
};
getReceiptsData();
processReceipts();
}, []);
const processReceipts = () => {
const dubps = [];
const resultObj = {};
receipts.map((item) => {
if (dubps.includes(item.merchant_name)) {
resultObj[item.merchant_name] =
resultObj[item.merchant_name] + parseFloat(item.total);
} else {
resultObj[item.merchant_name] = parseFloat(item.total);
dubps.push(item.merchant_name);
}
});
setResult(resultObj);
setKeys(Object.keys(resultObj));
};
const exportReport = async () => {
let token = await SecureStore.getItemAsync("token");
fetch("https://notrealapi/api/export", {
method: "GET",
headers: {
Authorization: `JWT ${JSON.parse(token)}`,
},
})
.then((res) => res.json())
.then((json) => {
console.log(json);
})
.catch((error) => console.error(error));
};
const renderSummary = () => {
return keys.map((key) => {
return (
<View>
<Text
key={key}
style={{
fontSize: 15,
fontWeight: "normal",
paddingBottom: 50,
}}
>
{`You have spent $${result[key].toString()} at ${key}`}
</Text>
</View>
);
});
};
return (
<View style={{ flex: 1, alignItems: "center", justifyContent: "center" }}>
{loading ? (
<ActivityIndicator size="large" color="blue" />
) : (
<>
<Text style={{ fontSize: 30, fontWeight: "bold", paddingBottom: 50 }}>
Summary:
</Text>
{renderSummary()}
<Button
type="outline"
title="Export detailed report"
onPress={exportReport}
/>
<Text style={{ fontSize: 10, marginTop: 10 }}>
*The detailed report shall be sent by email.
</Text>
</>
)}
</View>
);
};
export default Receipts;
Note: It does work but only when I save the file and it refreshes using expo CLI. Also, error occurs in the renderSummary() function.
Update: keys can be equal to ["Costco"] and result can be equal to {Costco: 69.99}
You are running processReceipts() before the fetch within getReceiptsData() has resolved.
Notice the order of the console logs in this example.
import React, { useEffect, useState } from "react";
const Receipts = () => {
const [receipts, setReceipts] = useState([]);
const [loading, setLoding] = useState(true);
const [result, setResult] = useState({});
const [keys, setKeys] = useState([]);
useEffect(() => {
const getReceiptsData = async () => {
fetch("https://rickandmortyapi.com/api/character/1", {
method: "GET"
})
.then((res) => res.json())
.then((json) => {
console.log("getReceiptsData resolves");
setReceipts(json);
setLoding(false);
})
.catch((error) => console.error(error));
};
getReceiptsData(); // js won't wait here
processReceipts();
}, []);
const processReceipts = (json) => {
console.log("processReceipts()");
};
return null;
};
export default Receipts;
Instead, handle the data manipulation when the fetch resolves.
import React, { useEffect, useState } from "react";
const Receipts = () => {
const [loading, setLoding] = useState(true);
const [result, setResult] = useState({});
useEffect(() => {
const getReceiptsData = async () => {
fetch("https://rickandmortyapi.com/api/character/1", {
method: "GET"
})
.then((res) => res.json())
.then((json) => {
console.log("getReceiptsData resolves");
processReceipts(json);
setLoding(false);
})
.catch((error) => console.error(error));
};
getReceiptsData();
}, []);
const processReceipts = (json) => {
console.log("processReceipts()");
// do some work and then setResult
};
return null;
};
export default Receipts;
Also, avoid storing a state that is derived from another state where possible. You should either translate the server payload into usable data:
when you receive the payload then set it to state OR
when you are rendering

Pausing react native expo Audio?

Im having no problem running audio but im having trouble pausing it. If anyone can help that would be great.
async function playMusic() {
const soundObject = new Audio.Sound();
console.log("Being hit")
try {
await soundObject.loadAsync({uri:'https://firebasestorage.googleapis.com/v0/b/o/myFolder%2FMello%20C%20-%20Easy.mp3?c4e-4bf8-b8ea-dcc201efff44'});
await soundObject.playAsync();
} catch (error) {
alert("Error" + error.message)
}
}
async function stopMusic() {
console.log("Not Being hit")
const soundObject = new Audio.Sound();
try {
await soundObject.loadAsync({uri:'https://firebasestorage.googleapis.com/v03?alt=media&token=e44f7b2f-8c4e-4bf8-b8ea-dcc201efff44'});
await soundObject.stopAsync();
} catch (error) {
alert("Error" + error.message)
}
}
See we should never create Sound instance as a state variable. Because whenever there is a re-render, it re-initializes the sound instance.
You should create a ref variable and use it,
const sound = React.useRef(new Audio.Sound());
Working Example
Implementation of Pause/Play music
import * as React from 'react';
import {
Text,
View,
StyleSheet,
ActivityIndicator,
Button,
} from 'react-native';
import Constants from 'expo-constants';
import { Audio } from 'expo-av';
const SampleTrack = require('./Roses.m4a');
export default function App() {
const [Loaded, SetLoaded] = React.useState(false);
const [Loading, SetLoading] = React.useState(false);
const sound = React.useRef(new Audio.Sound());
React.useEffect(() => {
LoadAudio();
}, []);
const PlayAudio = async () => {
try {
const result = await sound.current.getStatusAsync();
if (result.isLoaded) {
if (result.isPlaying === false) {
sound.current.playAsync();
}
}
} catch (error) {}
};
const PauseAudio = async () => {
try {
const result = await sound.current.getStatusAsync();
if (result.isLoaded) {
if (result.isPlaying === true) {
sound.current.pauseAsync();
}
}
} catch (error) {}
};
const LoadAudio = async () => {
SetLoading(true);
const checkLoading = await sound.current.getStatusAsync();
if (checkLoading.isLoaded === false) {
try {
const result = await sound.current.loadAsync(SampleTrack, {}, true);
if (result.isLoaded === false) {
SetLoading(false);
console.log('Error in Loading Audio');
} else {
SetLoading(false);
SetLoaded(true);
}
} catch (error) {
console.log(error);
SetLoading(false);
}
} else {
SetLoading(false);
}
};
return (
<View style={styles.container}>
<View style={styles.AudioPLayer}>
{Loading ? (
<ActivityIndicator size={'small'} color={'red'} />
) : (
<>
{Loaded === false ? (
<>
<ActivityIndicator />
<Text>Loading Song</Text>{' '}
</>
) : (
<>
<Button title="Play Song" onPress={PlayAudio} />
<Button title="Pause Song" onPress={PauseAudio} />
</>
)}
</>
)}
</View>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
paddingTop: Constants.statusBarHeight,
backgroundColor: '#ecf0f1',
padding: 8,
},
AudioPLayer: {
width: '100%',
height: 50,
alignItems: 'center',
},
});

Resources