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() }, []) }