Issue
So I'm rendering filtered products, and now I want to create pagination. It is working but I need to click a page number first before it shows up. I already included a loading state, but it's not working properly.
My data is coming from the backend MongoDB
const ProductList = ({products, category}) => {
const [filteredProducts, setFilteredProducts] = useState([])
const [loading, setLoading] = useState(true)
useEffect(() =>{
const isAvailable = products.filter((product) => product.quantity !== 0 )
setFilteredProducts(isAvailable)
setLoading(false)
},[setFilteredProducts, category,products])
const firstIndex = 0
const [pageSize, setPageSize] = useState(5)
const [page, setPage] = useState(1)
const [data,setData] = useState(filteredProducts.slice(firstIndex, pageSize))
useEffect(() =>{
setData(filteredProducts.slice(0, pageSize))
setLoading(false)
},[pageSize])
const handleChange = (event, value) => {
setPage(value);
setData(filteredProducts.slice(firstIndex + pageSize * (value - 1), pageSize * value));
};
return (
<>
{loading ?
<BeatLoader
color="#36d7b7"
loading={loading}
size={50}
aria-label="Loading Spinner"
data-testid="loader"
/>
:
(
<Box sx={{backgroundColor: '#f5f5f5', display:'flex', marginTop:2}}>
<Container maxWidth="xl">
<Typography sx={{textAlign: 'center', paddingY: '20px', fontWeight: 700, color: '#212121', typography: {xs: "h6", md: "h4"}}}>Products</Typography>
<Box sx={{display: 'flex', alignItems:'center',justifyContent: 'space-evenly', flexWrap: 'wrap', gap: '10px'}}>
{data.map((product) => (
<ProductItem key={product._id} product={product} />
))}
</Box>
<Pagination
sx={{display: 'flex', alignItems:'center',justifyContent:'center',margin: 4}}
size="large"
count={Math.ceil(filteredProducts.length/pageSize)}
page={page}
onChange={handleChange}
/>
</Container>
</Box>
)
}
</>
)
}
export default ProductList
Solution
It looks like the second useEffect
is using the value of filteredProducts
but not having it in the dependencies array, so it could not update data
when it is ready or if it changes.
This seems to be the reason data
could only be updated with the handleChange
event.
To fix this, try add filteredProducts
to the dependencies array:
useEffect(() => {
setData(filteredProducts.slice(0, pageSize));
setLoading(false);
// 👇 Add this here
}, [filteredProducts, pageSize]);
There might be other issues that need to be addressed, but hope that this still helps.
As for loading
, not too sure but it seems to start as true
but is immateriality set to false
as soon as useEffect
runs. If this component is not toggling the loading
value, it might not need to be a state.
Perhaps consider to render the spinner based on condition, for example if product
is empty []
(usually it is when the data is being fetched).
Alternatively perhaps handle a loading
state in the parent component where the data is fetched could also work, such as {loading ? <Spinner /> : <ProductList />}
.
Answered By - John Li
Answer Checked By - - Candace Johnson (ReactFix Volunteer)