Skip to content
Advertisement

how to stop multiple re-renders from doing multiple api calls useEffect?

I’m new to react functional components and I’m trying to get the weather data on multiple cities on page load but useEffect is now re-rending each call. How can I write this so useEffect doesn’t cause re-renders?

function App() {
    const [data, setData] = useState([]);
    const [activeWeather, setActiveWeather] = useState([]);

    useEffect(() => {
        const key = process.env.REACT_APP_API_KEY;

        const fetchData = async (city) => {
            const res = await axios.get(`https://api.openweathermap.org/data/2.5/weather?q=${city}&appid=${key}`);
            setData((data) => [
                ...data,
                { description: res.data.weather[0].description, icon: res.data.weather[0].icon, temp: res.data.main.temp, city: res.data.name, country: res.data.sys.country, id: res.data.id },
            ]);
        };
        const fetchCities = () => {
            const cities = [fetchData("Ottawa"), fetchData("Toronto"), fetchData("Vancouver"), fetchData("California"), fetchData("London")];

            Promise.all(cities).catch((err) => {
                console.log(err);
            });
        };
        fetchCities();
    }, []);

Advertisement

Answer

You can make the fetchData function to return the data you need without updating the state, then you can fetch x amount of cities and only when all of the requests complete update the state.

Note that if one of the requests inside Promise.all fail, it will go to the catch block without returning any data back, basically all or nothing

const key = process.env.REACT_APP_API_KEY

const fetchCity = async city => {
  const { data } = await axios.get(
    `https://api.openweathermap.org/data/2.5/weather?q=${city}&appid=${key}`,
  )

  return {
    description: data.weather[0].description,
    icon: data.weather[0].icon,
    temp: data.main.temp,
    city: data.name,
    country: data.sys.country,
    id: data.id,
  }
}

function App() {
  const [cities, setCities] = useState([])
  const [activeWeather, setActiveWeather] = useState([])

  useEffect(() => {
    const fetchCities = async () => {
      const citiesData = await Promise.all(
        ['Ottawa', 'Toronto', 'Vancouver'].map(fetchCity),
      )

      setCities(prevState => prevState.concat(citiesData))
    }

    fetchCities()
  }, [])
}
User contributions licensed under: CC BY-SA
3 People found this is helpful
Advertisement