jcobo1
jcobo1

Reputation: 1180

Display data from API using react component and useEffect

I have this react component which every time it's rendered show the country information that receives via props (country) and using the weather stack API must show also the capital weather at the current time. The first part (displaying the country data that comes from the props) works fine but I'm struggling to get the data from the weather API. I see on console that I'm getting the current weather but can't assign it to weather variable using setState() therefore my app crashes.

This is the component code I have so far, I've tried using async/await and .then synataxis in case I was mispelling something but always I get the same result:

const CountryDetails = async ({country}) => {
    
    const [weather, setWeather] = useState({});

    // const hook = async () => {
    //   const result = await axios.get(`http://api.weatherstack.com/current?access_key=${WEATHER_API}&query=${country.capital}`);
    //   console.log(result.data);
    //   setWeather(result.data.current);
    //   console.log(weather);
    // }

    const hook = () => {
      axios.get(`http://api.weatherstack.com/current?access_key=${WEATHER_API}&query=${country.capital}`).then((response) => {
        console.log('then');
        setWeather({
          temperature: response.data.current.temperature,
          img: response.data.current.weather_icons,
          wind: response.data.current.wind_speed,
          dir: response.data.current.wind_direction
        });
        console.log(response.data.current);
      });
    }

    useEffect(hook, []);

    console.log(weather);

    return (
      <>
        <h1>{country.name}</h1>
        <p>capital {country.capital}</p>
        <p>population {country.population}</p>
        <h2>languages</h2>
        <ul>
          {country.languages.map((lang) => {
            <li key={lang.name}>{lang.name}</li>;
          })}
        </ul>
        <img src={country.flag}></img>
        <h2>Weather in {country.capital}</h2>
        <p><b>temperature: </b>{weather.current.temperature}</p>
        <img src={weather.current.weather_icons} />
        <p><b>wind: </b>{weather.current.wind_speed} direction {weather.current.wind_direction}</p>
      </>
    );
  };

sandbox with the whole code: https://codesandbox.io/s/vigilant-ride-h3t1j

Upvotes: 1

Views: 304

Answers (2)

Kiran
Kiran

Reputation: 350

Here is a codesandbox I created playing around with your code. Since you stated that you're receiving the data from the API successfully, I'm mocking that with my getWeather function. In addition to what @Viet answered, there were other issues in the code you provided. See if this helps or if the error still persists, please provide with a reproduced example of the snippet:

https://codesandbox.io/s/competent-dhawan-fds81?file=/src/App.js:52-62

import { useEffect, useState } from "react";

const getWeather = (country) => {
  return Promise.resolve({
    data: {
      current: {
        temperature: "<temperature>",
        weather_icons: "<weather_icons>",
        wind_speed: "<wind_speed>",
        dir: "<wind_direction>"
      }
    }
  });
};

const CountryDetails = ({ country }) => {
  const [weather, setWeather] = useState({});

  const hook = () => {
    getWeather(country).then((response) => {
      console.log("then");
      setWeather({
        temperature: response.data.current.temperature,
        img: response.data.current.weather_icons,
        wind: response.data.current.wind_speed,
        dir: response.data.current.dir,
        wind_speed: response.data.current.wind_speed
      });
      console.log(response.data.current);
    });
  };

  useEffect(hook, [country]);

  // You should get {} logged here, not undefined
  console.log(weather);

  return (
    <>
      <h1>{country.name}</h1>
      <p>Capital: {country.capital}</p>
      <p>Population: {country.population}</p>
      <h2>Languages</h2>
      <ul>
        {/* You were not returning anything in the callback of the map function */}
        {country.languages.map((lang, i) => (
          <li key={i}>{lang.name}</li>
        ))}
      </ul>
      <img src={country.flag}></img>
      <h2>Weather in {country.capital}</h2>
      <p>
        <b>temperature: </b>
        {/* As @Veit mentioned, you were accessing the wrong property */}
        {weather.temperature}
      </p>
      <img src={weather.weather_icons} />
      <p>
        <b>Wind: </b>
        {weather.wind_speed} Direction: {weather.dir}
      </p>
    </>
  );
};

export default (props) => {
  const country = {
    languages: [{ name: "<name>" }],
    flag: "<flag name>",
    capital: "<capital name>",
    name: "<Coutry Name>",
    population: "<POPULATION>"
  };
  return <CountryDetails country={country} />;
};

Upvotes: 1

Viet
Viet

Reputation: 12807

You are just extracting wrong properties from weather state. This works:

import axios from "axios";
import { useState, useEffect } from "react";

const WEATHER_API = "xxx";

const CountryDetails = ({ country }) => {
  const [weather, setWeather] = useState({});
 
  const hook = () => {
    axios
      .get(
        `http://api.weatherstack.com/current?access_key=${WEATHER_API}&query=${country.capital}`
      )
      .then((response) => {
        console.log("then", response);
        setWeather({
          temperature: response.data.current.temperature,
          img: response.data.current.weather_icons,
          wind: response.data.current.wind_speed,
          dir: response.data.current.wind_dir
        });
        console.log(JSON.stringify(weather));
      });
  };

  useEffect(hook, []);

  console.log(weather);

  return (
    <>
      <h2>languages</h2>
      <p><b>temperature: </b>{weather.temperature}</p>
      <p>
        <b>wind: </b>
        {weather.wind} direction {weather.dir}
      </p>
    </>
  );
};

export default function App() {
  return (
    <div className="App">
      <CountryDetails country={{ capital: "London" }} />
    </div>
  );
}

Upvotes: 0

Related Questions