Jim41Mavs
Jim41Mavs

Reputation: 572

React useEffect not behaving as expected

I'm using OpenWeatherAPI, i got the fetch working fine. I'm able to useState to put that successful fetch data into the state. The console log shows up and i can see the request made in the network tab.

However there is something funny going on, my .map()ed data isn't rendering every time as i expect it to. I will write the code, press save and it will show up on the screen. However if i refresh page or restart server it just doesn't show up. Sometimes it shows up after a few refreshes.

I'm most likely doing something wrong the hooks system. Please point out what i'm doing incorrectly.

I can't just directly use the list i put in state after the promise is resolved, i need to filter out the response i just set in state and only get the keys/vals i need hence why you see the second state for filteredForecasts. Why is it only periodically working now and then? I feel like i have all the correct null check if statements yet it still doesn't work as expected...

import React from "react";
import WeatherCard from '../WeatherCard';
import "./WeatherList.scss";

const WeatherList = (props) => {
  return (
    <div className="weather-list-container">
      <WeatherCard />
    </div>
  );
};

export default WeatherList;
import React, { useState, useEffect } from "react";
import "./WeatherCard.scss";
import { getForecast } from "../../api/GET/getForecast";

const WeatherCard = () => {
  const [forecasts, setForecasts] = useState([]);
  const [filteredForecasts, setFilteredForecasts] = useState([]);

  useEffect(() => {
    getForecast()
      .then((res) => {
        const { list } = res;
        setForecasts(list);
      })
      .catch((err) => {
        console.log(err);
      });
  }, []);

  useEffect(() => {

    if (forecasts.length) {
      const uniqueForecasts = Array.from(
        new Set(allRelevantData.map((a) => a.day))
      ).map((day) => {
        return allRelevantData.find((a) => a.day === day);
      });

      setFilteredForecasts(uniqueForecasts);
    }
  }, []);

  const allRelevantData = Object.entries(forecasts).map(([key, value]) => {
    const dateTime = new Date(value.dt * 1000);
    const day = dateTime.getDay();

    const item = {
      day: day,
      temp: Math.round(value.main.temp),
      weatherMetaData: value.weather[0],
    };

    return item;
  });


  return filteredForecasts && filteredForecasts.map(({ day, temp, weatherMetaData }) => {
    return (
      <div className="weather-card">
        <div className="day-temperature-container">
          <span className="day">{day}</span>
          <span className="temperature">{temp}</span>
        </div>
        <div className="weather-description">
          <span
            className="icon weather"
            style={{
              background: `url(http://openweathermap.org/img/wn/${weatherMetaData.icon}.png)`,
            }}
          />
          <p>{weatherMetaData.description}</p>
        </div>
      </div>
    );
  });
};

export default WeatherCard;
import openWeatherConfig from '../../config/apiConfig';

const {baseUrl, apiKey, londonCityId} = openWeatherConfig;

export function getForecast(cityId = londonCityId) {
  return fetch(`${baseUrl}/forecast?id=${cityId}&units=metric&appid=${apiKey}`)
    .then(res => res.json());
}

Upvotes: 1

Views: 543

Answers (2)

ray
ray

Reputation: 27245

You're passing an empty dependency array to your second (filtered forecasts) useEffect call, which means it will run only when the component mounts. If your first effect hasn't returned yet, your filtered forecasts will never see any data.

You probably don't need the second useEffect call at all. Just compute it when the forecasts come back in the first effect.

Upvotes: 0

Prateek Thapa
Prateek Thapa

Reputation: 4938

PROBLEM

useEffect only runs on mount when it an empty array dependency in which case it might be highly likely the forecast is empty.

SOLUTION

filteredForecast is a derivative property of forecast state. Remove it from the state and use it without the React.useEffect.

  const allRelevantData = Object.entries(forecasts).map(([key, value]) => {
    const dateTime = new Date(value.dt * 1000);
    const day = dateTime.getDay();

    const item = {
      day: day,
      temp: Math.round(value.main.temp),
      weatherMetaData: value.weather[0],
    };

    return item;
  });

  let filteredForecasts = null; 
   
    if (forecasts.length) {
      filteredForecasts = Array.from(
        new Set(allRelevantData.map((a) => a.day))
      ).map((day) => {
        return allRelevantData.find((a) => a.day === day);
      });

  return /** JSX **/

Upvotes: 1

Related Questions