currently im having a weird issue with regards to rendering my API. (im using Expo GO)
Im using Axios, which calls the API inside of a UseEffect once I get location of the device. The problem I'm facing is that the screen only renders the fetched data once i save or cntrl s on VS Code.
Ive tried to log the array, get nothing once the function is called. Ive also tried adding a button to call the function instead, only then it renders, which makes sense as it triggers a re-render.
WHEN I ENTER THE COMPONENT: enter image description here
WHEN I SAVE THE FILE, IT THEN FETCHES DATA: enter image description here
Would be much appreciated if anyone can help, maybe im missing some basic knowledge of how this works. Thanks guys!
import axios from 'axios';
import { SafeAreaView, StyleSheet, Text, View, Button, Alert, ScrollView, RefreshControl } from 'react-native';
import React, { Component, useState, useEffect, createContext } from 'react';
import { winners } from '../screens/MainScreen';
import * as Location from 'expo-location';
import { useIsFocused } from '@react-navigation/native';
const API = () => {
const resturant = winners;
const isFocused = useIsFocused();
const [NamesFound, setNamesFound] = useState(0);
const APIKey ='xxxxxxxxxx';
const [Result, SetResult] = useState([]);
const [Names, SetNames] = useState([]);
const [lat, Setlat] = useState();
const [long, Setlong] = useState();
// const [location, setLocation] = useState(null);
const [errorMsg, setErrorMsg] = useState(null);
const [address, setAddress] = useState(null);
const [RestAddress, setRestAddress] = useState([]);
const [refreshing, setRefreshing] = useState(false);
const [loading, setLoading] = useState(true);
const wait = (timeout) => {
return new Promise(resolve => setTimeout(resolve, timeout));
}
const onRefresh = React.useCallback(() => {
setRefreshing(true);
wait(2000).then(() => setRefreshing(false));
}, []);
const SampleFunction=(item)=>{
Alert.alert(item);
}
const getLocation = () => {
(async () => {
let { status } = await Location.requestForegroundPermissionsAsync();
if (status !== 'granted') {
setErrorMsg('Permission to access location was denied');
}
Location.setGoogleApiKey(APIKey);
// console.log(status)
let { coords } = await Location.getCurrentPositionAsync();
// setLocation(coords);
Setlat(coords.latitude);
Setlong(coords.longitude);
if (coords) {
let { longitude, latitude } = coords;
let regionName = await Location.reverseGeocodeAsync({
longitude,
latitude,
});
setAddress(regionName[0].city);
console.log(regionName[0].city);
}
})();
};
var config = {
method: 'get',
url: 'https://maps.googleapis.com/maps/api/place/textsearch/json?location=' lat ',' long '&key=' APIKey '&status=&query=' resturant,
headers: { }
};
useEffect(() => {
const fetchData = async () =>{
setLoading(true);
try {
const {data: response} = await axios(config);
SetResult(response.results);
console.log(Result.map((name)=> name.name));
SetNames(Result.map((name)=> name.name))
setNamesFound(Names.length);
console.log('API function called.')
console.log (Names);
} catch (error) {
console.error(error.message);
}
setLoading(false);
}
getLocation();
fetchData();
}, []);
return(
<SafeAreaView style={styles.container}>
{loading && <Text>Loading....</Text>}
{!loading && isFocused && (
<><Text style={styles.MainText}>{NamesFound} {winners}s within {address}</Text><ScrollView
style={styles.scroll}
refreshControl={<RefreshControl
refreshing={refreshing}
onRefresh={onRefresh} />}
>
{Names.map((item, key) => (
<Text key={key} style={styles.TextStyle} onPress={SampleFunction.bind(this, item)}> {item} </Text>)
)}
</ScrollView></>
)}
{/* <Button title='CLICK ME FOR API' onPress={CallAPI} style={styles.btn}></Button> */}
</SafeAreaView>
);
};
CodePudding user response:
You have to change these lines
useEffect(() => {
const fetchData = async () =>{
setLoading(true);
try {
const {data: response} = await axios(config);
SetResult(response.results);
console.log(Result.map((name)=> name.name));
SetNames(Result.map((name)=> name.name))
setNamesFound(Names.length);
console.log('API function called.')
console.log (Names);
} catch (error) {
console.error(error.message);
}
setLoading(false);
}
getLocation();
fetchData();
}, []);
to something like this
useEffect(() => {
const fetchData = async () =>{
setLoading(true);
try {
const {data: response} = await axios(config);
const newResult = response.results;
SetResult(newResult);
const newNames = newResult.map((name)=> name.name);
console.log(newNames);
SetNames(newNames)
setNamesFound(newNames.length);
console.log('API function called.')
console.log (newNames);
} catch (error) {
console.error(error.message);
}
setLoading(false);
}
getLocation();
fetchData();
}, []);
Please notice how I use newResult and newName, which are derived from the immediate response, to set the states instead of using the state variable Result and Names.
To understand the reason,
- You have to know that setting a state variable by using its setter (e.g.
SetResult,SetNameswill not immediately assign the value to the variable.
// This is the 1st time React calls your function
const [Result, SetResult] = useState([]); // Now Result = []
const [Names, SetNames] = useState([]); // Also Names = []
useEffect(() => {
const fetchData = async () =>{
setLoading(true);
try {
const {data: response} = await axios(config);
SetResult(response.results);
// Result still = [] here
SetNames(Result.map((name)=> name.name))
// Names still = [], likewise
console.log('API function called.')
console.log (Names);
} catch (error) {
console.error(error.message);
}
setLoading(false);
}
getLocation();
fetchData();
}, []);
- React "re-render" when the state variable's setter is called, and the value is then assigned to the variable.
// This is the 2nd time React calls your function
// , and it does so because you call SetResult
const [Result, SetResult] = useState([]); // Now Result = [...] <-- now it has value
const [Names, SetNames] = useState([]); // Also Names = [] <-- but this is still an empty list
// This is the 3rd time React calls your function
// , and it does so because you call SetNames
const [Result, SetResult] = useState([]); // Now Result = [...] <-- now it has value
const [Names, SetNames] = useState([]); // Also Names = [...] <-- now it has value
If you really want to make a state variable to depend on another variable, you can do it like this (though not need for the case in your question).
const [Result, SetResult] = useState([]);
const [Names, SetNames] = useState([]);
useEffect(() => {
const fetchData = async () =>{
setLoading(true);
try {
const {data: response} = await axios(config);
SetResult(response.results);
} catch (error) {
console.error(error.message);
}
setLoading(false);
}
fetchData();
}, []);
useEffect(() => {
SetNames(Result.map((name)=> name.name))
}, [Result]);
In your case, I think it's better to do like this
const [result, setResult] = useState([]);
const names = result.map((name)=> name.name);
const namesFound = names.length;
useEffect(() => {
const fetchData = async () =>{
setLoading(true);
try {
const {data: response} = await axios(config);
setResult(response.results);
} catch (error) {
console.error(error.message);
}
setLoading(false);
}
fetchData();
}, []);
Tip: I rename the variables to make the leading characters lower case, this is the convention, and it will make your code more readable to other developers.
