skip to Main Content

I’m encountering an issue with my React application where I’m unable to update the weather details based on the selected city using the OpenWeatherMap API. Here’s the setup:

  1. I have a parent component App which manages the state of latitude and longitude.

  2. Inside App, I have a child component DisplayFetch that fetches weather details based on latitude and longitude.

  3. I also have another child component SearchCities inside DisplayFetch, which allows users to search for a city and updates the latitude and longitude when a city is selected.

The problem arises when I select a new city in SearchCities. Although the latitude and longitude are updated correctly in the App component, the weather details displayed in DisplayFetch do not update accordingly. The weather details remain associated with the previous location, rather than updating to the weather of the newly selected city.

I’ve ensured that the updateLocation function in the App component correctly updates the state of latitude and longitude, and it is being passed down to DisplayFetch and further to SearchCities. Additionally, I’ve confirmed that the update function (passed from DisplayFetch to SearchCities) is being called when a new city is selected, and it is correctly updating the latitude and longitude.

App component:

import React, { useState, useEffect } from "react";
import DisplayFetch from "./components/DisplayFetch";

export default function App() {
  const [latitude, setLatitude] = useState(null);
  const [longitude, setLongitude] = useState(null);

  useEffect(() => {
    navigator.geolocation.getCurrentPosition(
      (position) => {
        console.log(position.coords.latitude);
        console.log(position.coords.longitude);
        setLatitude(position.coords.latitude);
        setLongitude(position.coords.longitude);
      },
      (error) => {
        console.error("Error getting current geolocation:", error);
      }
    );
  }, []);

  const updateLocation = (lat, long) => {
    setLatitude(lat);
    setLongitude(long);
  };

  return (
    <div className="flex items-center justify-center h-screen bg-darkSlate">
      <div>
        {latitude} {longitude}
      </div>
      {latitude !== null && longitude !== null ? (
        <DisplayFetch
          latitude={latitude}
          longitude={longitude}
          updateLocation={updateLocation}
        />
      ) : (
        <p>Loading...dasdasdasdasdas</p>
      )}
    </div>
  );
}

DisplayFetch component:

import React, { useState, useEffect } from "react";
import WeatherBox from "./WeatherBox";
import { CiLocationOn } from "react-icons/ci";
import { AsyncPaginate } from "react-select-async-paginate";
import SearchCities from "./SearchCities";

const DisplayFetch = ({ latitude, longitude, updateLocation }) => {
  const [data, setData] = useState([]);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(null);

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

  const fetchData = async () => {
    try {
      const response = await fetch(
        `https://api.openweathermap.org/data/2.5/weather?lat=${latitude}&lon=${longitude}&appid=MYAPI`
      );
      // Replace with your API endpoint
      if (!response.ok) {
        throw new Error("Network response was not ok.");
      }
      const data = await response.json();
      setData(data);
      setLoading(false);
    } catch (error) {
      setError(error.message);
      setLoading(false);
    }
  };
  return (
    <>
      {loading ? (
        <p>Loading...</p>
      ) : error ? (
        <p>Error: {error}</p>
      ) : (
        <div className="bg-paleBlue p-8 rounded-lg shadow-lg flex flex-col">
          <div className="relative border border-solid border-gray-300 rounded-md mb-4">
            <div>
              {latitude} {longitude}
            </div>
            <SearchCities
              latCity={latitude}
              longCity={longitude}
              update={updateLocation} // Pass updateLocation here
            />
            <div className="absolute inset-y-0 right-0 flex items-center pr-2"></div>
          </div>
          <div className="grid grid-cols-2 gap-4">
            <WeatherBox title="Min Temp" value={`${data.main.temp_min}°C`} />
            <WeatherBox title="Max Temp" value={`${data.main.temp_max}°C`} />
            <WeatherBox
              title="Feels Like"
              value={`${data.main.feels_like}°C`}
            />
            <WeatherBox title="Pressure" value={`${data.main.pressure} hPa`} />
            <WeatherBox title="Humidity" value={`${data.main.humidity}%`} />
            <WeatherBox title="Wind Speed" value={`${data.wind.speed} m/s`} />
          </div>
        </div>
      )}
    </>
  );
};

export default DisplayFetch;

SearchCities component.

import React, { useState, useEffect } from "react";
import { AsyncPaginate } from "react-select-async-paginate";
import { geoApiOptions, GEO_API_URL } from "../components/api";
import DisplayFetch from "./DisplayFetch";

const SearchCities = ({ latCity, longCity, update }) => {
  const [search, setSearch] = useState(null);

  const loadOptions = (inputValue) => {
    return fetch(
      `${GEO_API_URL}/cities?minPopulation=1000&namePrefix=${inputValue}`,
      geoApiOptions
    )
      .then((response) => response.json())
      .then((response) => {
        return {
          options: response.data.map((city) => {
            return {
              value: `${city.latitude} ${city.longitude}`,
              label: `${city.name}, ${city.countryCode}`,
            };
          }),
        };
      });
  };

  const handleOnChange = (searchData) => {
    setSearch(searchData);

    console.log(latCity);
    console.log(longCity);
    if (searchData) {
      const [lat, long] = searchData.value.split(" ");
      update(lat, long);
    } else {
      // Clear the latitude and longitude when no city is selected
      update(null, null);
    }
    console.log(latCity);
    console.log(longCity);
  };
  return (
    <>
      <AsyncPaginate
        placeholder="Search for city"
        debounceTimeout={600}
        value={search}
        onChange={handleOnChange}
        loadOptions={loadOptions}
      />
      <>
        <div>
          {latCity} from search city{longCity}
        </div>
      </>
    </>
  );
};

export default SearchCities;

Can someone find where I am going wrong? I can’t seem to find the problem

2

Answers


  1. It looks like you’re missing the dependencies array in your useEffect.

     useEffect(() => {
      fetchData();
     }, [latitude, longitude, updateLocation]);
    

    This will trigger the fetchData() function on changes of any of this variables. Adjust accordingly (maybe you need the dependency only on updateLocation).

    Login or Signup to reply.
  2. The issue is the fetch function inside the useEffect you are calling it one time only, but you need to call everytime there is a change in lat and long.

    Inside DisplayFetch component you need to add dependencies (lat and long) to the useEffect, and you can conditionally re-fetch if there are lat and long.

    Here is how you do it

      useEffect(() => {
        if (latitude && longitude) fetchData();
      }, [latitude, longitude]);
    

    I created a codesandbox with mock data and mock fetch. You can check it here
    https://codesandbox.io/p/sandbox/flamboyant-tesla-t3mmm5

    You can fork the codesandbox in order to keep testing if you want.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search