cypher
cypher

Reputation: 13

React: Error: Too many re-renders. React limits the number of renders to prevent an infinite loop

this is my first time on stackoverflow, nice to meet y'all. I am pretty new to react. I am trying to learn hooks with a weather app I just built and I get this too many re-renders error. The app worked just fine with class components. I tried wrappign the handleInputChange in an arrow function but then the app didn't even call the api

This is the code:

import React, { useState } from "react";
import axios from "axios";
import "./App.css";

const dateBuilder = (today) => {
  let months = [
    "January",
    "February",
    "March",
    "April",
    "May",
    "June",
    "July",
    "August",
    "September",
    "October",
    "November",
    "December",
  ];
  let days = [
    "Sunday",
    "Monday",
    "Tuesday",
    "Wednesday",
    "Thursday",
    "Friday",
    "Saturday",
  ];
  let day = days[today.getDay()];
  let date = today.getDate();
  let month = months[today.getMonth()];

  return `${day} ${date} ${month}`;
};

const key = process.env.REACT_APP_WEATHER_API_KEY;

const App = () => {

  const [info, setInfo] = useState([]);

  const [background, setBackground] = useState("app");

  const handleInputChange = (event) => {
    console.log('event', event)
    if (event.key === "Enter") {
      const city = event.target.value;
      const apiUrl = `https://api.openweathermap.org/data/2.5/weather?q=${city}&appid=${key}`;

      axios
        .get(apiUrl)
        .then((res) => {
          const { data } = res;
          console.log("api response", res);

            setInfo(data);
          console.log("info", info);
        })

        .catch((err) => console.log("err", err));
    }
  };

  if (info.weather) {
    switch (info.weather[0].main) {
      case "Rain":
        setBackground("rain");
        break;
      case "Clouds":
        setBackground("clouds");
        break;
      case "Snow":
        setBackground("snow");
        break;
      case "Fog":
        setBackground("fog");
        break;
      case "Drizzle":
        setBackground("drizzle");
      case "Thunderstorm":
        setBackground("thunderstorm");
      case "Clear":
        setBackground("app");
        break;
    }
  }

  return (
    <div className={background}>
      <main>
        <div className="search-box">
          <input
            type="text"
            className="search-bar"
            placeholder="search..."
            onKeyPress={handleInputChange}
          />
        </div>
        <div className="location-box">
          <div className="location">{info.name}</div>
          <div className="date">{dateBuilder(new Date())}</div>
        </div>
        {typeof info.main != "undefined" ? (
          <div>
            <div className="weather-box">
              <div className="temp">
                {Math.round(info.main.temp - 273.15)}º C
              </div>

              <div className="weather">{info.weather[0].main}</div>
              <div className="pressure">{info.main.pressure} mbar</div>
              <div className="pressure">{info.main.humidity} % humidity</div>
            </div>
          </div>
        ) : (
          <></>
        )}
      </main>
    </div>
  );
};

export default App;

Upvotes: 1

Views: 162

Answers (1)

Alexandre
Alexandre

Reputation: 2050

Hi and welcome to stackoverflow community!

In that case you need to use an effect ! Your component loop again and again because of your switch statement.

setBackground is called on every render if info.weather exist. But calling setBackground trigger an new render also. What you need is an effect depending on the value of info

const App = () => {

  const [info, setInfo] = useState([]);

  const [background, setBackground] = useState("app");

  useEffect(() => {
    if (info.weather) {
      switch (info.weather[0].main) {
        case "Rain":
          setBackground("rain");
          break;
        case "Clouds":
          setBackground("clouds");
          break;
        case "Snow":
          setBackground("snow");
          break;
        case "Fog":
          setBackground("fog");
          break;
        case "Drizzle":
          setBackground("drizzle");
        case "Thunderstorm":
          setBackground("thunderstorm");
        case "Clear":
          setBackground("app");
          break;
      }
    }}, [info])

  const handleInputChange = (event) => {
    console.log('event', event)
    if (event.key === "Enter") {
      const city = event.target.value;
      const apiUrl = `https://api.openweathermap.org/data/2.5/weather?q=${city}&appid=${key}`;

      axios
        .get(apiUrl)
        .then((res) => {
          const { data } = res;
          console.log("api response", res);

            setInfo(data);
          console.log("info", info);
        })

        .catch((err) => console.log("err", err));
    }
  };

  return (
    <div className={background}>
      <main>
        <div className="search-box">
          <input
            type="text"
            className="search-bar"
            placeholder="search..."
            onKeyPress={handleInputChange}
          />
        </div>
        <div className="location-box">
          <div className="location">{info.name}</div>
          <div className="date">{dateBuilder(new Date())}</div>
        </div>
        {typeof info.main != "undefined" ? (
          <div>
            <div className="weather-box">
              <div className="temp">
                {Math.round(info.main.temp - 273.15)}º C
              </div>

              <div className="weather">{info.weather[0].main}</div>
              <div className="pressure">{info.main.pressure} mbar</div>
              <div className="pressure">{info.main.humidity} % humidity</div>
            </div>
          </div>
        ) : (
          <></>
        )}
      </main>
    </div>
  );
};

More informations about Effect : https://reactjs.org/docs/hooks-effect.html

Upvotes: 1

Related Questions