Graffis
Graffis

Reputation: 45

Chain API calls with React hook useEffect

I need a solution for the following sequence:

The browser checks user's Geolocation (assuming he allows that) -> Longitude and Latitude are kept in state and used for 2 API calls -> Google Reverse Geolocation API checks for city name AND at the same time DarkSky API checks for the weather -> third API waits for results from previous calls and uses it as the query for the third, Unsplash API

Here's my code:

const [position, setPosition] = useState({ latitude: '50.049683', longitude: '19.944544' });
const [info, setInfo] = useState({ city: null, weather: null });
const [photos, setPhotos] = useState([]);

useEffect(() => {
    const fetchInfo = async () => {
      try {
        const [cityInfo, weatherInfo] = await Promise.all([
          axios.get(
            `https://maps.googleapis.com/maps/api/geocode/json?latlng=${position.latitude},${position.longitude}&language=en&result_type=locality&key=${GEO_ACC_KEY}`,
          ),
          axios.get(
         `https://api.darksky.net/forecast/${WEATHER_ACC_KEY}/${position.latitude},${position.longitude}?exclude=hourly,daily,alerts,flags`,
          ),
        ]);
        setInfo({
          city: cityInfo.data.results[0].address_components[0].short_name,
          weather: weatherInfo.data.currently.summary.toLowerCase(),
        });

        console.log('Info', info); // Results in {city: null, weather: 'null'}

        const photosData = await axios.get(
          `https://api.unsplash.com/search/photos?query=${info.weather}+${info.city}&page=1&per_page=8&client_id=${UNSPLASH_ACC_KEY}`,
        );

        setPhotos(photosData.data.results);

        console.log('Photos data from API call:', photosData); //Object based on query: "null+null"
        console.log('Photos:', photos); // Empty array
      } catch (err) {
        // Handling errors
      }
    };
    fetchInfo();
  }, []);

  console.log('Info outside axios get', info); // Results in object with city name and current weather
  console.log('photos outside axios get', photos); // Proper result I am looking for

Right now the proper data is available ONLY outside useEffect. It does not provide the data for the third API call (right now Unsplash API call uses "null+null" as a query).

So I headed over to useEffect documentation and it says that a second parameter (an array) takes dependencies and updates when any of the state dependencies change.

I tried using it as follows:

useEffect(() => {
    const fetchInfo = async () => {
      //rest of the code
},
fetchInfo();
}, [info]);

It uses proper keywords for the API call (city and weather, instead of null null) BUT creates an infinite API calls.

How do I fix that?

Upvotes: 0

Views: 611

Answers (1)

Shubham Khatri
Shubham Khatri

Reputation: 281626

State updates are not immediate and will reflect in the next render cycle.

Please check this post for more details on this: useState set method not reflecting change immediately

Also you must note that you want to chain API call and not call the entire useEffect again on info change. Adding info as a dependency will definitely lead to an infinite loop since info is being set inside the useEffect.

To solve your problem, You can instead use the value you set to state while making the api call

    const newInfo = {
      city: cityInfo.data.results[0].address_components[0].short_name,
      weather: weatherInfo.data.currently.summary.toLowerCase(),
    }

     setInfo(newInfo);

    console.log('Info', info); // Results in {city: null, weather: 'null'}

    const photosData = await axios.get(
      `https://api.unsplash.com/search/photos?query=${newInfo.weather}+${newInfo.city}&page=1&per_page=8&client_id=${UNSPLASH_ACC_KEY}`,
    );

Upvotes: 1

Related Questions