Home > Mobile >  Async Function with Promise.All Only Running After Updating Code
Async Function with Promise.All Only Running After Updating Code

Time:05-02

I'm writing an asynchronous function in React that should set the "coords" state to an array of objects containing US states and their respective latitudes/longitudes. To do this, I'm making an API call and resolving those calls using Promise.all. On the first refresh of the page, the function works as intended. However, on subsequent refreshes, the function does not execute the Promise.all statement and therefore does not set the coords state. Why is this the case and how can I resolve this issue?

export default function App() {

  const [covidDataJSON, setCovidDataJSON] = useState(null);
  const [stateNames, setStateNames] = useState([]);
  const [coords, setCoords] = useState([]);
  const [stateInfos, setStateInfos] = useState([]);

  useEffect(() => {
    fetchCoords();
  }, [])

  const fetchCoords = async () => {
    try {
      getStateNames();
      const req = await Promise.all(stateNames.map(async (state) => {
        return await axios.get(`https://nominatim.geocoding.ai/search.php?state=${state}&format=jsonv2`);
      }))
      for (let i = 0; i < req.length; i  ){
        const stateInfo = req[i].data[0];
        if (req[i].data.length !== 0)
          setCoords(coordsArr => [...coordsArr, {name: stateInfo.display_name, lat: stateInfo.lat, lon: stateInfo.lon}]);
      }
    } catch (err) {
      console.error(err);
    }
  };

  const getStateNames = () => {
    try {
      const stateArr = [];
      for (let i = 0; i < states.length; i  ){
        stateArr.push(states[i].name);
      }
      setStateNames(stateArr);
    } catch (error) {
      console.error(error);
    }
  }

CodePudding user response:

I think your promise.all does not execute because stateNames is still an empty array.

You need to define 2 useEffect hooks as follows. -

// this will trigger function fetchCoords once your have set the stateNames.
useEffect(() => {
    if(!stateNames || !stateNames.length){
        return;
    }
    fetchCoords();
}, [stateNames]);

// this hook will set your stateNames once the component loads.
useEffect(() => {
    getStateNames();
}, [])

Also, I don't see the states variables used in getStateNames defined in above sample code. Also remove getStateNames() call from inside of fetchCoords() Let me know if the above works for you and try logging stateNames to verify this.

CodePudding user response:

  1. Inside getStateNames(), you're calling setStateNames(stateArr); setter function for a state is async. You're mapping over stateNames inside fetchCoords(). There is a possibility that stateNamesstate is not updated when map loop is run. Thats why I return stateArr from getStateNames() and use that value inside fetchCoords().

  2. Inside fetchCoords(), you've added the following code


const req = await Promise.all(stateNames.map(async (state) => {
        return await axios.get(`https://nominatim.geocoding.ai/search.php?state=${state}&format=jsonv2`);
      }))

There is no need to await axios.get() here because you're already using Promise.all to await for promises to resolve, that's why I've removed await axios.get() from .map and simply returning an array of promises for Promise.all()

Give this a try. I think this should work.


export default function App() {
  const [covidDataJSON, setCovidDataJSON] = useState(null);
  const [stateNames, setStateNames] = useState([]);
  const [coords, setCoords] = useState([]);
  const [stateInfos, setStateInfos] = useState([]);

  useEffect(() => {
    fetchCoords();
  }, []);

  const fetchCoords = async () => {
    try {
      const stateArr = getStateNames();
      const req = await Promise.all(
        stateArr.map((state) => {
          return axios.get(
            `https://nominatim.geocoding.ai/search.php?state=${state}&format=jsonv2`
          );
        })
      );

      for (let i = 0; i < req.length; i  ) {
        const stateInfo = req[i].data[0];
        if (req[i].data.length !== 0)
          setCoords((coordsArr) => [
            ...coordsArr,
            {
              name: stateInfo.display_name,
              lat: stateInfo.lat,
              lon: stateInfo.lon
            }
          ]);
      }
    } catch (err) {
      console.error(err);
    }
  };

  const getStateNames = () => {
    try {
      const stateArr = [];
      for (let i = 0; i < states.length; i  ) {
        stateArr.push(states[i].name);
      }
      setStateNames(stateArr);
      return stateArr;
    } catch (error) {
      console.error(error);
    }
  };

  return (
    <div className="App">
      <h1>Hello CodeSandbox</h1>
      <h2>Start editing to see some magic happen!</h2>
    </div>
  );
}


  • Related