Home > Net >  React- trying to call two Api's from the same state?
React- trying to call two Api's from the same state?

Time:06-30

So I'm trying to create a simple enough weather app using React. I've decided that there's going to be one search box where the user types in the city name (using an onchange on the input)and when submitted, it will:

1: Change the background image to show a random image of that city from the UnSplash API. (I coded this first and it works fine.) 2. It will show relevant weather details below such as temp, wind speed etc.

 function App() {
      const [city, setCity] = useState("");
    
      const fetchCityImage = (e) => {
        e.preventDefault();
        axios
          .get(
            `https://api.unsplash.com/photos/random?query=${city}&client_id={API_KEY}`
          )
          .then((response) => {
            console.log(response.data);
            setCity(response.data.urls.regular);
          });
      };
    
      const fetchCityWeather = (e) => {
        e.preventDefault();
        axios
          .get(
            `https://api.openweathermap.org/data/2.5/weather?q=${city}&units=metric&appid=${OTHER_API_KEY}`
          )
          .then((response) => {
            console.log(response.data);
          });
      };   
return (
    <div className="App">
      {/* HEADER */}
      <div className="bg-black w-full h-[600px]">
        <img
          src={city}
          alt="city image"
          className="w-full h-[600px] object-cover"
        />
        {/* Search and Info */}
        <div className="absolute w-full h-[600px] top-[0%] flex flex-col justify-center bg-gradient-to-t from-black">
          <div className="flex justify-center py-4">
            <h1 className="font-bold">Weather App</h1>
          </div>

          <div className="flex justify-center">
            <form
              onSubmit={fetchCityWeather}
              className="flex justify-between border border-white rounded-xl max-w-[300px] p-4"
            >
              <input
                onChange={(e) => setCity(e.target.value)}
                className="bg-transparent focus:outline-none placeholder:text-white"
                type="text"
                placeholder="Search a City..."
              />
              <button onClick={fetchCityWeather}>
                <BsSearch size={20} />
              </button>
            </form>
          </div>
        </div>
      </div>

      {/* DATA */}
    </div>
  );
}

export default App;

This is where I run into a problem. I can log my weather data to the console but how do I go about linking that API call to the same input field. Or the same state even to use that data on the rest of the page? I've been switching out the onClick and onSubmit to get the two different results I want but need them together.

I hope I explained that well enough. Thanks guys.

CodePudding user response:

To use the same state for both you want to change city from a string to an object. City should look something like this: { weather: 'sunny', img: 'cityImgSrc' } that way when you update city you can just update the properties you want to change and to use it would be as simple as city.img and city.weather

To "link" the API calls you can just call fetchCityImage in your .then after your console log. Or the better way would probably be to create one function and use async await that way you only update state once, after you get all your data back.

CodePudding user response:

I think you should try a much more React-like approach. Here are some points to consider:

  • Create a clear distinction between the name of the desired city, now a proper state named city and the data you need to your app. You enter the city name and when it changes, the app goes to fetch the needed data.
  • As @Jawwaad Sabree proposed in his answer, use an object to store the data fetched.

I've made some changes to your code, to make it a bit more "React way". Haven't tested this, because I don't have API keys, but I believe you can make it work with just a few changes.

Hope this helps. But if not, please post your doubts as comments and I'll try to help.

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

function App() {
  const [city, setCity] = useState("");
  const [appData,setAppData] = useState({});
   
  const handleCityChange = (e) => {
    e.preventDefault();
    const cityInput = document.getElementById("city-input");
    setCity(cityInput.value);
  }

  useEffect(async (e) => {
    e.preventDefault();

    const imageData = await axios.get(`https://api.openweathermap.org/data/2.5/weather?q=${city}&units=metric&appid=${OTHER_API_KEY}`);
    const weatherData = await axios.get(`https://api.openweathermap.org/data/2.5/weather?q=${city}&units=metric&appid=${OTHER_API_KEY}`);

    setAppData({ image: imageData.data.urls.regular, weather: weatherData.data });
  },[city]);

  return (
    <div className="App">
      {/* HEADER */}
      <div className="bg-black w-full h-[600px]">
        <img
          src={city}
          alt="city image"
          className="w-full h-[600px] object-cover"
        />
        {/* Search and Info */}
        <div className="absolute w-full h-[600px] top-[0%] flex flex-col justify-center bg-gradient-to-t from-black">
          <div className="flex justify-center py-4">
            <h1 className="font-bold">Weather App</h1>
          </div>

          <div className="flex justify-center">
            <input
              id="city-input"
              className="bg-transparent focus:outline-none placeholder:text-white"
              type="text"
              placeholder="Search a City..."
            />
            <button onClick={handleCityChange}>New city</button>
          </div>
        </div>
      </div>

      {/* DATA */}
    </div>
  );
}

export default App;
  • Related