Home > Software design >  Cant render my component data correctly in React Native - Using Axios
Cant render my component data correctly in React Native - Using Axios

Time:02-08

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,

  1. You have to know that setting a state variable by using its setter (e.g. SetResult, SetNames will 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();
    
  }, []);

  1. 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.

  •  Tags:  
  • Related