Home > OS >  How to access Data returning by Axios with async/await
How to access Data returning by Axios with async/await

Time:10-11

I have two functions using Axios to make requests of data to an API. Within those functions I also return the response that is received by the API. Both of those functions do use async/await. From my understanding, since Axios returns a promise, I have to await the function calls to unpack the promise and retrieve the data. Am I calling these functions incorrectly? The goal is to use the values returned by these functions to render a React-Leaflet component. However, React it seems to be reading the values as null. My assumption is that when the functions are called within the component, the Promise is being returned instead of the value, even though I am calling the function with await. Below is the code I have so far...

import { MapContainer, TileLayer, Marker, Popup, Circle } from 'react-leaflet';
import mockData from './testData/MOCK_DATA.json'
import axios from 'axios';
import outageData from './testData/outageData.json'

const fillBlueOptions = { fillColor: 'blue' };

async function convertAddressLat(location){//uses the geocodify api to get lat and long of an address
        const resp = await axios.get('https://api.geocodify.com/v2/geocode/json?api_key=mykey&q='   location.outage_street  ', '   location.outage_city  ', Michigan, USA');
        return Number(resp.data.response.bbox[1]);

}

async function convertAddressLong(location){
    const resp = await axios.get('https://api.geocodify.com/v2/geocode/json?api_key=mykey&q='   location.outage_street  ', '   location.outage_city  ', Michigan, USA');
    return Number(resp.data.response.bbox[2]);
}

/// This returns the actual value I am looking to pass into the react component
(async function(){
    let val = await convertAddressLat(outageData.outages[6]);
    console.log("Inside function 1: " val);
})();

//this is still returning a promise. I have tried returning the await and also the method below
console.log("inside function 2: " (async function(){
    let x = await convertAddressLat(outageData.outages[6]);
    return x;
})());

function OutageMap() { //This is where the map page will be rendered.
    return (
        <MapContainer center={[38.89, -77.059]} zoom={13} scrollWheelZoom={true}>
            <TileLayer
                attribution='&copy; <a href="http://osm.org/copyright">OpenStreetMap</a> contributors'
                url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
            />

            {outageData.outages.map(mock => (
                <Circle center={[
                    (async function(){
                        let lat = await convertAddressLat(mock);
                        return lat;})(), 
                    (async function(){
                        let long = await convertAddressLong(mock);
                        return long;})()
                    ]} 
                    pathOptions={fillBlueOptions} radius={200}>
                    <Popup>{mock.description}</Popup>
                </Circle>
            ))}
        </MapContainer>
    );
  }
  
  export default OutageMap;

I have referenced this post How to return the response from an asynchronous call on how unwrap a promise, but I am confused how I can call the function within the React component.

CodePudding user response:

Put those functions inside the component. Remove async/await and replace it with then, catch block. For example if we had async function 'fetch' returning the value it would look like:

fetch().then(response => {
   do sth
}).catch(err => {
   console.log(err);
})

Invoke the function in

useEffect(() => {
    fetch().then().catch()
},[]) 

hook. If you pass empty dependencies list it will behave like componentDidMount. In then block assign returned values to state (it will rerender the component).

useEffect(() => {
    fetch().then(res => {
        setState(res.data);
    }).catch()
},[]) 

In the view check if your state has the values: if not render 'No values or sth'. You can also add loading feedback. You set loading in state to true before invoking async function and set to false in then/catch block.

useEffect(() => {
    setLoading(true);
    fetch().then(res => {
        setState(res.data);
        setLoading(false);
    }).catch(err => {
        setLoading(false);
    })
},[]) 

Then you can provide some feedback based on loading state.

CodePudding user response:

async functions always return a Promise. So in this block you're awaiting the convertAddressLat Promise but the anonymous inline function also returns a Promise.

center={[
  (async function(){
    let lat = await convertAddressLat(mock);
    return lat;})()

One common strategy for rendering asynchronously loaded data is to do the fetching in a useEffect hook that sets state once the Promise is resolved:

function OutageIndicator ({ outage }) {
    const [coords, setCoords] = useState();

    useEffect(() => {
        async function resolveLocation() {
          const resp = await axios.get( ... );
          const [lat, lng] = resp.data.response.bbox;
          setCoords({ lat, lng })
        }

        resolveLocation();
    }, [outage]);

    return !coords ? "Loading" : (
        <Circle center={[coords.lat, coords.lng]} />
    )
}

function OutageMap () {
    return (
        <MapContainer center={[38.89, -77.059]} zoom={13} scrollWheelZoom={true}>
            {outageData.outages.map(outage => (
                <OutageIndicator outage={outage} />
            )}
        </MapContainer>
    )
}

CodePudding user response:

As i see that you are currently using functional components. So you can use 'useEffect' hook in that function to call api for example

function myFuncComponent() {
  const [data,setData] = useState();
  useEffect(async () => {
   const { data } = await axios.get('my url');
   setData(data);
  })
}

If you want to call these async calls in a react component you simply have to define a state variable which your component changes that and you will handle the change with a async function or in a useEffect.

  • Related