Search code examples
reactjsreact-hooksreact-state-management

ReactJS img not updating after API Call


Im learning React right now and trying to wrap my head around why my other components updated the information but my img tag has not after the second API call.

Here's my code:

export default function LandingPage() {

    const [zipcode, setZipCode] = useState('');
    const [loading, setLoading] = useState(false);
    const [weatherData, setWeatherData] = useState();
    var [cityWeatherData, setCityWeatherData] = useState([]);
    var [forecast, setForcast] = useState([]);

   return(
 
<TextField 
  label='Zip Code' 
  value={zipcode} 
  onChange={(e) => { setZipCode(e.target.value) }} />
     <Button
       sx={{ ml: 3, backgroundColor: '#5F8FFF', color: 'white', '&:hover': { color: '#5F8FFF' } }}
      
       onClick={ () => {
           currentWeather(zipcode, apiKey)
           .then( async (result) => {
               setLoading(true);
               await sevenDayWeather(result['coord']['lon'], result['coord']['lat'], apiKey)
                      .then( (response)  => {
                                response['daily'].forEach( (day) => {
                                    console.log('day forecast: ', day);
                                    console.log('Day Weather: ', day['weather'][0]['icon']);
                                    setForcast( forecast => [...forecast, day['weather'][0]['icon']]);
                                })
                            });
                        });
                    }}>
      Search
    </Button>
     
    {loading ?
        // console.log('forecast: ', forecast)
            <WeatherBox
                apiKey={apiKey}
                name={weatherData['name']}
                lat={weatherData['coord']['lat']}
                lon={weatherData['coord']['lon']}
                feelsLike={weatherData['main']['feels_like']}
                highestTemp={weatherData['main']['temp_max']}
                lowestTemp={weatherData['main']['temp_min']}
                forecast={forecast}
            /> : <></>}
);}

For my WeatherBox component

export default function WeatherBox(props) {
let newDate = new Date()
let date = newDate.getDate();
let month = newDate.getMonth() + 1;
let year = newDate.getFullYear();
return (
    <Box
        className='retrievedInformation'
        sx={{
            mt: 10,
            p: 5,
            boxShadow: 'gray 5px 10px 10px 5px',
            borderRadius: '20px',
            textAlign: 'end',
            backgroundImage: `url(${sunny})`,
            objectFit: 'contain',
            backgroundRepeat: 'no-repeat',
            backgroundSize: '500px 500px'
        }}>
        <Typography
            sx={{ fontSize: '50px' }}>
            {props.name}
        </Typography>
        <Typography
            sx={{ fontSize: '25px' }}>
            Today:  {month} / {date} / {year}
        </Typography>
            <img src={`http://openweathermap.org/img/wn/${props.forecast[0]}@2x.png`} alt='forecast image' />
        <Box
            display='flex'
            flexDirection='row'
            sx={{ textAlign: 'end', justifyContent: 'end', alignItems: 'end' }}>
            <Typography
                sx={{ mr: 3, fontSize: '30px', fontWeight: '300', color: 'gray' }}>
                Feels Like:
            </Typography>
            <Typography
                sx={{ fontSize: '30px' }}>
                {props.feelsLike} F
            </Typography>
        </Box>
        <Box
            display='flex'
            flexDirection='row'
            justifyContent='end'
            alignItems='end'
            sx={{ textAlign: 'end' }}>
            <Typography
                sx={{ mr: 3, fontSize: '20px', fontWeight: '300', color: 'gray', textAlign: 'end' }}>
                Highest Temperature:
            </Typography>
            <Typography
                sx={{ fontSize: '20px', textAlign: 'end' }}>
                {props.highestTemp} F
            </Typography>
        </Box>
        <Box
            display='flex'
            flexDirection='row'
            justifyContent='end'
            alignItems='end'>
            <Typography sx={{ mr: 3, fontSize: '20px', fontWeight: '300', color: 'gray', textAlign: 'end' }}>Lowest Temperature: </Typography>
            <Typography sx={{ fontSize: '20px', textAlign: 'end' }}> {props.lowestTemp} F</Typography>
        </Box>
        <Box textAlign='end' alignItems='end' justifyContent='end'>
            <Typography sx={{ mt: 5, fontSize: '30px' }}>Weather forecast for the next 7 days</Typography>
            <img src={`http://openweathermap.org/img/wn/${props.forecast[1]}@2x.png`} alt='forecast image' />
            <img src={`http://openweathermap.org/img/wn/${props.forecast[2]}@2x.png`} alt='forecast image' />
            <img src={`http://openweathermap.org/img/wn/${props.forecast[3]}@2x.png`} alt='forecast image' />
            <img src={`http://openweathermap.org/img/wn/${props.forecast[4]}@2x.png`} alt='forecast image' />
            <img src={`http://openweathermap.org/img/wn/${props.forecast[5]}@2x.png`} alt='forecast image' />
            <img src={`http://openweathermap.org/img/wn/${props.forecast[6]}@2x.png`} alt='forecast image' />
            <img src={`http://openweathermap.org/img/wn/${props.forecast[7]}@2x.png`} alt='forecast image' />
        </Box>
    </Box>
);
}

My forecast array has been update as well and holding all the correct values however, the img tag in weatherbox is still not updating

Thanks for your help in advance

EDIT: Added link to codesandbox

https://codesandbox.io/s/blissful-thunder-u76vwt?file=/src/App.js


Solution

  • Issue

    The img tag loads all the images fine for the first call however, the problem is that when I do another zipcode and clicked search, the texts updated, but the img tag (the weather images) did not update ( i.e. first search 91001 everything looks great, searched again for 95133, name changed to San Jose but the weather forecast images did not update from 91001's to 95133's)

    You always append forecast data to the forecast state but only reference the first 8 elements.

    response["daily"].forEach((day) => {
      setForcast((forecast) => [
        ...forecast, // <-- shallow copy persists old forecast data
        day["weather"][0]["icon"]
      ]);
    });
    

    ...

    <img
      src={`http://openweathermap.org/img/wn/${props.forecast[0]}@2x.png`}
      alt="forecast image"
    />
    
    ...
    
    <img
      src={`http://openweathermap.org/img/wn/${props.forecast[1]}@2x.png`}
      alt="forecast image"
    />
    <img
      src={`http://openweathermap.org/img/wn/${props.forecast[2]}@2x.png`}
      alt="forecast image"
    />
    <img
      src={`http://openweathermap.org/img/wn/${props.forecast[3]}@2x.png`}
      alt="forecast image"
    />
    <img
      src={`http://openweathermap.org/img/wn/${props.forecast[4]}@2x.png`}
      alt="forecast image"
    />
    <img
      src={`http://openweathermap.org/img/wn/${props.forecast[5]}@2x.png`}
      alt="forecast image"
    />
    <img
      src={`http://openweathermap.org/img/wn/${props.forecast[6]}@2x.png`}
      alt="forecast image"
    />
    <img
      src={`http://openweathermap.org/img/wn/${props.forecast[7]}@2x.png`}
      alt="forecast image"
    />
    

    Solution

    To resolve, you should overwrite the forecast state when updating.

    setForcast(response.daily.map((day) => day.weather[0].icon));
    

    Here's a "optimized" button click handler.

    <Button
      onClick={async () => {
        setLoading(false);
    
        const weather = await currentWeather(zipcode, apiKey);
        setWeatherData(weather);
        console.log(weather);
    
        const forecast = await sevenDayWeather(
          weather.coord.lon,
          weather.coord.lat,
          apiKey
        );
        setForcast(forecast.daily.map((day) => day.weather[0].icon));
    
        setLoading(true);
      }}
    >
      Search
    </Button>
    

    And for the sake of DRY-ness, mapped forecast images.

    {props.forecast.map((id, index) => (
      <img
        key={index}
        src={`http://openweathermap.org/img/wn/${id}@2x.png`}
        alt="forecast image"
      />
    ))}