Need some help handling the response I'm getting from json server - react-native

I'm developing an app in React Native in which I'm trying to display some data provided by a fake API I set up using json server. I'm using the useContext hook to handle the general state of the app and since I'm fairly new to React Native and React in general I need some help handling the response I'm manipulating through the context API.
This is the State file I set up in the context folder
import React, { useReducer } from 'react'
import MenusReducer from './MenusReducer'
import MenusContext from './MenusContext'
import { baseUrl } from '../../shared/baseURL'
const MenusState = (props) => {
const initialState = {
menus: [],
selectedMenu: null
}
const [state, dispatch] = useReducer(MenusReducer, initialState)
const getMenus = async () => {
const response = await fetch(baseUrl + 'RESTAURANTES')
const data = await response.json()
console.log('This is the reducer working'); // This is a test log to see if it works
dispatch({
type: 'GET_MENUS',
payload: data
})
}
const getDetails = async (id) => {
const response = await fetch(`${baseUrl}RESTAURANTES/${id}`)
const data = await response.json()
dispatch({
type: 'GET_DETAILS',
payload: data
})
}
return (
<MenusContext.Provider value={{
menus: state.menus,
selectedMenu: state.selectedMenu,
getMenus,
getDetails
}}>
{props.children}
</MenusContext.Provider>
)
}
export default MenusState;
So here I set up a getMenus() function by which I get all the items I'd like to display in my components. As you can see, I put a test log inside the function to see if it works, which it does.
The problem comes when I try to get those items inside my app components. Here's one of the instances in which I try to get the items to display.
const Home = ({ navigation }) => {
const { menus, getMenus } = useContext(MenusContext)
const [search, setSearch] = useState('')
const [response, setResponse] = useState([])
const [categories, setCategories] = useState(allCategories)
const [loading, setLoading] = useState(true)
useEffect(() => {
const data = async () => await getMenus();
console.log('This is the app executing');
setLoading(false);
setResponse(data)
console.log(response);
}, [])
// ... some code later
return (
<ScrollView style={styles.yScroll}>
<View>
<Text style={styles.sectionTitle}>Destacados</Text>
</View>
<ScrollView horizontal showsHorizontalScrollIndicator={false}>
<View style={styles.sectionContainer}>
<Text>{response[0]}</Text> // Here's where I'm trying to print something about the response but it's not working
</View>
</ScrollView>
<View>
<Text style={styles.sectionTitle}>Categorias</Text>
</View>
<View style={styles.sectionContainer}>
{categories.map((item, index) => {
return (
<View key={index} style={styles.category}>
<Text>{item}</Text>
</View>
)
})}
</View>
</ScrollView>
)
}
So inside one of the ScrollViews I'm setting up a test to see if the response can be displayed, which it is not. However, inside the useEffect, I'm setting up a test log with the message 'This is the app executing' which is working, BUT, the response being logged is an empty array.
I'm sure the problem I'm facing has something to do with the asynchronous response between app and server, but I have no clear idea as to how I can address this.
Can someone please point me in the right direction? Thanks in advance!!

Based on your code, I think you can do this
const Home = ({ navigation }) => {
const { menus, getMenus } = useContext(MenusContext)
const [search, setSearch] = useState('')
const [categories, setCategories] = useState(allCategories)
const [loading, setLoading] = useState(true)
useEffect(() => {
const data = async () => await getMenus();
console.log('This is the app executing');
data();
setLoading(false);
}, [])
// ... some code later
return (
<ScrollView style={styles.yScroll}>
<View>
<Text style={styles.sectionTitle}>Destacados</Text>
</View>
<ScrollView horizontal showsHorizontalScrollIndicator={false}>
<View style={styles.sectionContainer}>
<Text>{menus[0]}</Text> // Here's where I'm trying to print something about the response but it's not working
</View>
</ScrollView>
<View>
<Text style={styles.sectionTitle}>Categorias</Text>
</View>
<View style={styles.sectionContainer}>
{categories.map((item, index) => {
return (
<View key={index} style={styles.category}>
<Text>{item}</Text>
</View>
)
})}
</View>
</ScrollView>
)
}

Related

How to use asyncStorage inside useEffect

I'm building a mobile game using react native and I'm trying to retrieve the best value storage on it to display on the screen. The problem is that it seems that react native is rendering the screen before it retrieves the value and then it doesn't re-render when the value is updated using setBest(), so no value is displayed.
Here is the code:
const navigation = useNavigation()
const [result, setResult] = useState('')
const [best, setBest] = useState('')
useEffect(() => {
const Storage = async (key,value) => {
await AsyncStorage.setItem(key,value)
}
const Retrieve = async (key) => {
const value = await AsyncStorage.getItem(key)
setBest(()=>value)
}
Retrieve('1').catch(console.error)
setResult(route.params.paramKey)
if(route.params.paramKey>best){
var aux = result.toString()
Storage('1',aux)
console.log(best)
}
}, [])
return (
<View style={styles.container}>
<View style={styles.textView}>
<Text style={styles.tituloText}>Melhor pontuação</Text>
<Text style={styles.tituloText}>{best}</Text>
<Text style={styles.tituloText}>Sua pontuação</Text>
<Text style={styles.resultText}>{result}</Text>
<View style={styles.viewBtn}>
<TouchableOpacity style={styles.viewBack} onPress={() => navigation.navigate('Modo1')}>
<Icon style={styles.iconBack} name="backward" />
</TouchableOpacity>
<TouchableOpacity style={styles.viewHome} onPress={() => navigation.dispatch(StackActions.popToTop)}>
<Icon style={styles.iconBack} name="home" />
</TouchableOpacity>
</View>
</View>
</View>
);
}
Thanks for the help guys! I've been struggling with this for days and any help will be appreciated!
This is how you retrieve the value..
useEffect(() => {
AsyncStorage.getItem('key').then(value => {
if (value != null) {
console.log(value);
setBest(value);
}
});
}, []);
also don't forget to add the import statement..
To set the value you must use
AsyncStorage.setItem('key', value);
You can use Async Functions inside of ~useEffect()` like this:
useEffect(() => {
(async () => {
async function getData() {
try {
const value = await AsyncStorage.getItem('myKey');
if (value !== null) {
setData(value);
}
} catch (error) {
console.log(error);
}
}
getData();
})();
}, []);
}

React Native - Can't find variable - conditional

I'm making an app that looks for a data in the Firebase database, and if the data exists it appears a Text Input. If the data doesn't exist, it shows another Text Input.
//CODE
function Veic({ navigation, route }) {
const [date, setDate] = useState("");
const database = firebase.firestore()
const [selectedValue, setSelectedValue] = useState("");
const [placa, setPlaca] = useState("");
const [atv, setAtv] = useState("");
const [km, setKm] = useState("");
const [obs, setObs] = useState("");
const [kmF, setKmF] = React.useState("");
const { idUser, user, uid } = route.params;
var auth = firebase.auth();
useEffect(() => { // the rest of the code doesn't read the "values_id"
const DocVeic = query(collection(database, "user_veic"),where("email", "==", auth.currentUser?.email),where("kmF", "==", ""));
getDocs(DocVeic).then((querySnapshot) => {
let values_id = null;
querySnapshot.forEach((doc) => {
values_id = doc.id;
console.log(`${values_id}`);
})
})
},[])
function altInfo(){
const Doc = query(collection(database, "user_veic"),where("email", "==", auth.currentUser?.email),where("kmF", "==", ""));
getDocs(Doc).then((querySnapshot) => {
let values = null;
querySnapshot.forEach((doc) => {
console.log(`${doc.id} => ${doc.data()}`);
values = doc.id;
});
var transactionUpdate = database.collection("user_veic").doc(values);
transactionUpdate.update({
kmF: kmF,
})
})
}
// ADD A DATA
function addInfo(){
database.collection("user_veic").add({
email: auth.currentUser?.email,
placa: placa,
atv: atv,
km: km,
obs: obs,
dataInicial: data_full,
dataFinal: '',
kmF: '',
});
navigation.navigate('Liber', {idUser: user})
}
return (
<View style={fundoVeic.container}>
<View style={fundoVeic.perfilUser}>
<Text style={{color:'#007831',fontWeight:'bold',fontSize:15,height: 40}}>
<Image style={fundoVeic.imageEnvel} source={require('../../imagens/envelope.png')}/>
<Text style={{color:'#cce4d5'}}>...</Text>{auth.currentUser?.email}
</Text>
</View>
{values_id === null ?
<View>
<TextInput
style={fundoVeic.input}
placeholder='Digite a placa do veículo'
maxLength={7}
placeholderTextColor='#000'
onChangeText={txtPlaca => setPlaca(txtPlaca)}
value={placa}
/>
<TextInput
style={fundoVeic.input}
placeholder='Digite a atividade'
placeholderTextColor='#000'
onChangeText={txtAtv => setAtv(txtAtv)}
value={atv}
/>
<TextInput
style={fundoVeic.input}
keyboardType='numeric'
placeholder='Digite o km do veículo'
placeholderTextColor='#000'
onChangeText={txtKm => setKm(txtKm)}
value={km}
/>
<TextInput
style={fundoVeic.inputObs}
multiline={true}
numberOfLines={5}
placeholder='Observação (opcional)'
placeholderTextColor='#000'
onChangeText={txtObs => setObs(txtObs)}
value={obs}
/>
<Pressable
style={fundoVeic.button}
onPress={addInfo}
>
<Text style={fundoVeic.textButton}>Selecionar</Text>
</Pressable>
</View>
:
<View>
<TextInput
keyboardType='numeric'
placeholder='Digite o km final do veículo'
placeholderTextColor='#000'
onChangeText={txtKmF => setKmF(txtKmF)}
value={kmF}
/>
<Pressable
onPress={altInfo}
>
<Text>Liberar</Text>
</Pressable>
</View>
}
</View>
);
}
export default Veic;
The app has a login. When logging in, enters a screen that will appear a certain text and input (with a condition). The condition: if the variable "kmf", in Firebase, is empty, it shows an input on the screen for the user to put a km. When the user writes the km, the data is updated. The problem is that I am not able to make a condition that reads the "values_id". What is the solution?
values_id is not component state. In fact the variable only exists inside the useEffect function and is not available anywhere else. Add it to the component state with useState.
const [valuesId, setValuesId] = useState<string>(null);
useEffect(() => {
// ...
setValuesId(doc.id);
},[])

error while playing the audio TypeError: sound.current.playAsync is not a function

I am trying to use expo/av to create a music player in React - Native . But i am Facing the error
error while playing the audio TypeError: sound.current.playAsync is not a function
Here is my code below :
import React, { useState, useEffect } from "react";
import { View, StyleSheet, TouchableOpacity, Text } from "react-native";
import { Audio } from "expo-av";
import Icon from "../icon/icon";
import { Slider } from "react-native-range-slider-expo";
import { AntDesign } from '#expo/vector-icons';
const SampleTrack = require("../../assets/audioFile/Hello.mp3")
const AudioPlayer = () => {
const [Loaded,SetLoaded] = useState(false);
const [Loading,SetLoading] = useState(false);
const [Playing,SetPlaying] = useState(false);
const sound = React.useRef(new Audio.Sound());
useEffect(()=>{
LoadAudio();
},[]);
const PlayAudio = async ()=>{
try{
const result = await sound.current.getStatusAsync();
if(result.isLoaded){
if(result.isPlaying === false){
sound.current.playAsync();
SetPlaying(true)
}
}
}catch(e){
console.log('error while playing the audio',e)
}
};
const PauseAudio = async() =>{
try{
const result = await sound.current.getStatusAsync();
if(result.isLoaded){
if(result.isPlaying === true){
sound.current.pauseAsync();
}
}
}catch(e){
console.log("error while pausing the audio",e)
}
}
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('Unknown error while loading audio')
}else{
SetLoading(false);
SetLoaded(true)
}
}catch(e){
console.log('error while loading the audio',e)
SetLoading(false)
}
}else{
console.log(checkLoading)
SetLoading(false)
}
}
return (
<View style={styles.container}>
{Playing ? <View style={styles.button}>
<TouchableOpacity onPress={() => PauseAudio()}>
<AntDesign name="pause" size={24} color="black" />
</TouchableOpacity>
</View> : <View style={styles.button}>
<TouchableOpacity onPress={() => PlayAudio()}>
<Icon name="playButton" fill={"#858787"} height={20} />
</TouchableOpacity>
</View>}
<View style={styles.lineContainer}>
{/* <View style={styles.line}>
<View style={styles.progressbar}></View>
<View style={styles.bulb}></View>
</View> */}
<Seekbar></Seekbar>
</View>
</View>
);
};
I am trying to use Async functions as per the documentation (used ref to init the audio class and using the require to get the audio for the local storage and it seems not working) but any of it is not working as seems ,I am seriously stuck . Any kind of help is appreciated.
I'm running into this same issue and haven't figured out how to get playAsync() to work yet, but a workaround I've found is to use setStatusAsync() instead of any of the convenience methods.
So you could try replacing:
sound.current.playAsync()
... with:
sound.current.setStatusAsync({ shouldPlay: true })

React-Native FlatList item clickable with data to another screen

I'm trying to access a screen when you click on an item in my flatlist by passing the date I retrieved from the firebase before, I've tried several things without success so I come to you.
Basically when I click on one of the elements -> A screen with details should appear.
export default function Notifications() {
const dbh = firebase.firestore();
const [loading, setLoading] = useState(true); // Set loading to true on component mount
const [deliveries, setDeliveries] = useState([]); // Initial empty array of users
useEffect(() => {
const subscriber = dbh
.collection("deliveries")
.onSnapshot((querySnapshot) => {
const deliveries = [];
querySnapshot.forEach((documentSnapshot) => {
deliveries.push({
...documentSnapshot.data(),
key: documentSnapshot.id,
});
});
setDeliveries(deliveries);
setLoading(false);
});
// Unsubscribe from events when no longer in use
return () => subscriber();
}, []);
if (loading) {
return <ActivityIndicator />;
}
return (
<FlatList
style={{ flex: 1 }}
data={deliveries}
renderItem={({ item }) => (
<TouchableOpacity
onPress={() => { * HERE I NEED TO PASS DATA AND SHOW AN ANOTHER SCREEN FOR DETAILS * }}>
<View style={styles.container}>
<Text>DATE: {item.when}</Text>
<Text>ZIP DONATEUR: {item.zip_donator}</Text>
<Text>ZIP BENEFICIAIRE: {item.zip_tob_deliv}</Text>
</View>
</TouchableOpacity>
)}
/>
);
}
EDIT: Small precision this screen is located in a Tab.Navigator
you can pass params in navigation,
export default function Notifications(props) {
const { navigation } = props
const dbh = firebase.firestore();
const [loading, setLoading] = useState(true); // Set loading to true on component mount
const [deliveries, setDeliveries] = useState([]); // Initial empty array of users
useEffect(() => {
const subscriber = dbh
.collection("deliveries")
.onSnapshot((querySnapshot) => {
const deliveries = [];
querySnapshot.forEach((documentSnapshot) => {
deliveries.push({
...documentSnapshot.data(),
key: documentSnapshot.id,
});
});
setDeliveries(deliveries);
setLoading(false);
});
// Unsubscribe from events when no longer in use
return () => subscriber();
}, []);
if (loading) {
return <ActivityIndicator />;
}
return (
<FlatList
style={{ flex: 1 }}
data={deliveries}
renderItem={({ item }) => (
<TouchableOpacity
onPress={() => {
navigation.navigate('screenName', {
//pass params here
})
}}>
<View style={styles.container}>
<Text>DATE: {item.when}</Text>
<Text>ZIP DONATEUR: {item.zip_donator}</Text>
<Text>ZIP BENEFICIAIRE: {item.zip_tob_deliv}</Text>
</View>
</TouchableOpacity>
)}
/>
);
}
you can access params in the navigated screen by props.route.params

React Native Function Component ASYNC / AWAIT Problem

sorry for bad English.
My function component not waiting API Function, I'm write async and await and not working again..
"Object are not valid as a React child" error screen...
Please help me :'/
const NormalCarousel = async (props) => {
const navigation = useNavigation();
const [ResponseData, setResponseData] = useState('');
const ComponentAPI = props.api;
const API = await axios.get(ComponentAPI).catch((error) => {alert(error)});
await setResponseData(API);
return(
<ScrollView horizontal={true} showsHorizontalScrollIndicator={false}>
{
ResponseData.map(item => (
<TouchableOpacity style={styles.CarouselTouchable} onPress={() => navigation.navigate("Ürün", {id: item.item_id})}>
<Image
style={styles.CarouselImage}
source={{uri: item?.item_avatar}}
/>
<View style={styles.CarouselView}>
<Text style={styles.CarouselTitle}>{item?.item_name}</Text>
<Text style={styles.CarouselSubtitle}>{item?.item_stock_code}</Text>
</View>
</TouchableOpacity>
))
}
</ScrollView>
)
}
The error is happening because the parent component trying to render this component is actually rendering a Promise which resolves into a component. That's not possible.
You should instead call the function to load the data once on component mount (useEffect). You'll also need to replace useState('') with useState([]) since you're trying to map over this data when rendering.
const NormalCarousel = (props) => {
const { api } = props;
const navigation = useNavigation();
const [responseData, setResponseData] = useState([]);
useEffect(() => {
getAPI();
}, []);
async function getAPI() {
const API = await axios.get(api).catch((error) => alert(error));
setResponseData(API);
}
return(
<ScrollView horizontal showsHorizontalScrollIndicator={false}>
{
responseData.map(item => (
<TouchableOpacity style={styles.CarouselTouchable} onPress={() => navigation.navigate("Ürün", {id: item.item_id})}>
<Image
style={styles.CarouselImage}
source={{uri: item.item_avatar}}
/>
<View style={styles.CarouselView}>
<Text style={styles.CarouselTitle}>{item.item_name}</Text>
<Text style={styles.CarouselSubtitle}>{item.item_stock_code}</Text>
</View>
</TouchableOpacity>
))
}
</ScrollView>
)
}