I am making an API call to firebase from useEffect and the data returned is empty when the page loads for the first time but gets populated if I make any changes to the code and save it. I want the data to be present when the page loads for the first time.
import React, { useState, useEffect } from "react";
import { Link } from "react-router-dom";
import { motion } from "framer-motion";
import { getDatabase, ref, onValue } from "firebase/database";
import Helmet from "../components/Helmet/Helmet";
import "../styles/home.css";
import { Container, Row, Col } from "reactstrap";
import heroImg from "../assets/images/hero-img.png";
import Services from "../services/Services";
import ProductsList from "../components/UI/ProductsList";
import Clock from "../components/UI/Clock";
import counterImg from "../assets/images/counter-timer-img.png";
const Home = () => {
const [isLoading, setIsLoading] = useState(true);
const [products, setProducts] = useState([]);
const [trendingProducts, setTrendingProducts] = useState([]);
const [bestSalesProducts, setBestSalesProducts] = useState([]);
const [mobileProducts, setMobileProducts] = useState([]);
const [wirelessProducts, setWirelessProducts] = useState([]);
const [popularProducts, setPopularProducts] = useState([]);
const year = new Date().getFullYear();
useEffect(() => {
const fetchProducts = () => {
const db = getDatabase();
const thumbnailRef = ref(db, "Contents/");
onValue(thumbnailRef, (snapshot) => {
const data = snapshot.val();
if (data !== null) {
Object.values(data).map((product) => {
return setProducts((oldArray) => [...oldArray, product]);
});
console.log(products);
}
});
};
const filteredTrendingProducts = products.filter(
(item) => item.category === "Kids"
);
const filteredBestSalesProducts = products.filter(
(item) => item.category === "Entertainment"
);
const filteredMobileProducts = products.filter(
(item) => item.category === "LifeStyle"
);
const filteredWirelessProducts = products.filter(
(item) => item.category === "wireless"
);
const filteredPopularProducts = products.filter(
(item) => item.category === "watch"
);
setTrendingProducts(filteredTrendingProducts);
setBestSalesProducts(filteredBestSalesProducts);
setMobileProducts(filteredMobileProducts);
setWirelessProducts(filteredWirelessProducts);
setPopularProducts(filteredPopularProducts);
setIsLoading(false);
fetchProducts();
}, []);
if (isLoading) {
return <div> Loading ... </div>;
}
return (
whileTap={{ scale: 1.2 }}
className="buy__btn store__btn"
>
<Link to="/shop">Visit Store</Link>
</motion.button>
</Col>
<Col lg="6" md="12" className="text-end counter__img">
<img src={counterImg} alt="" />
</Col>
</Row>
</Container>
</section>
<section className="new__arrivals">
<Container>
<Row>
<Col lg="12" className="text-center mb-5">
<h2 className="section__title">New Arrivals</h2>
</Col>
<ProductsList data={mobileProducts} />
<ProductsList data={wirelessProducts} />
</Row>
</Container>
</section>
<section className="popular_category">
<Container>
<Row>
<Col lg="12" className="text-center mb-5">
<h2 className="section__title">Popular in Category</h2>
</Col>
<ProductsList data={popularProducts} />
</Row>
</Container>
</section>
</Helmet>
);
};
export default Home;
I have tried adding products as the useEffect depencies but it creates an infinite loop fetching.
What do I have to do for the data to be fetched on the first page load?
2
Answers
It is normal if
console.log(products)
right aftersetProducts()
returnsundefined
becausesetProducts()
is asynchronous andproducts
is not updated yet when logged.Adding
products
as a dependency will create a infinite loop because the sameuseEffect
is both setting and listening toproducts
.However if you prefer, you could separate the logic for filters to a second
useEffect
and addproducts
as its dependency, since the filtered are actually dependent onproducts
.Example:
setState in react is async. Read more here. So when you set your state (products), it takes some time to actually update your state.
In order to fix your problem, you have 2 options.
First is to write another useEffect on your products state and move your logic there like this:
And the other way is to use async/await and store your products in a temp variable and use the variable instead of the state:
You can also delete async/await if the functions are not async.