Home > Net >  How to fix infinite loop in useEffect
How to fix infinite loop in useEffect

Time:12-03

So I am working on an online shop as a practice I have imported products data and wanna make pagination. On a page it will be 12 products like so enter image description here

I have this kind of code, but I don't understand why there is an infinite loop in useEffect and how to fix this The error is: "Maximum update depth exceeded. This can happen when a component calls setState inside useEffect, but useEffect either doesn't have a dependency array, or one of the dependencies changes on every render." I can rid of products in dependancy, but not sure it's the way

import React, {useState, useEffect} from "react";
import data from './products.json'
import Product from "./components/Product/Product";

const PRODUCTS_PER_PAGE = 12

export const Shop = () => {
    const [products, setProducts] = useState(data.products)
    const [currentPage, setCurrentPage] = useState(1)

    const firstIndex = (currentPage - 1) * PRODUCTS_PER_PAGE
    const lastIndex = firstIndex   PRODUCTS_PER_PAGE
    const totalPages = products.length / PRODUCTS_PER_PAGE


    useEffect(() => {
        const slicedProducts = products.slice(firstIndex,lastIndex)
        setProducts(slicedProducts)
        console.log(slicedProducts)
    }, [currentPage, products])

    return (
        <div className="products">
            {
                products.map((product) => (
                <Product product={product}/>))
            }
        </div>
    )
}

CodePudding user response:

Remove products from the dependency array

useEffect(() => {
  setProducts(prods => prods.slice(firstIndex,lastIndex))
}, [currentPage])

Edit:

If you want to slice before render, remove useEffect entirely

import React, {useState, useEffect} from "react";
import data from './products.json'
import Product from "./components/Product/Product";

const PRODUCTS_PER_PAGE = 12

export const Shop = () => {
    const [products, setProducts] = useState(data.products)
    const [currentPage, setCurrentPage] = useState(1)

    const firstIndex = (currentPage - 1) * PRODUCTS_PER_PAGE
    const lastIndex = firstIndex   PRODUCTS_PER_PAGE
    const totalPages = products.length / PRODUCTS_PER_PAGE

    return (
        <div className="products">
            {
                products.slice(firstIndex,lastIndex).map((product) => (
                <Product product={product}/>))
            }
        </div>
    )
}

CodePudding user response:

You can try using useMemo

// if you dont want to inlcude currentPage as a dependency and trigger another calculation
// const currentPageRef = useRef(currentPage);
// currentPageRef.current = currentPage;
const mappedProducts = useMemo(() => {

    const firstIndex = (currentPage - 1) * PRODUCTS_PER_PAGE
    const lastIndex = firstIndex   PRODUCTS_PER_PAGE

    const slicedProducts = products.slice(firstIndex, lastIndex)
    return slicedProducts

}, [products, currentPage])

return (
    <div className="products">
        {
         // should this be  mappedProducts?.map(product =>   
         mappedProducts.slice(firstIndex,lastIndex).map((product) => (
            <Product product={product}/>))
        }
    </div>
)

Ideally it could be nice it products could be filtered when making a fetch

Either ways hope this helps you in some way

CodePudding user response:

You have 2 options to fix your issue.

  1. Holding the previous data for products and currentPage and then compare them to their current value:
import React, {useState, useEffect, useRef} from "react";
import data from './products.json'
import Product from "./components/Product/Product";

const PRODUCTS_PER_PAGE = 12

export const Shop = () => {
    const [products, setProducts] = useState(data.products);
    const [currentPage, setCurrentPage] = useState(1);

    const previousProductsRef = useRef(data.products);
    const previousPageRef = useRef(1);

    const firstIndex = (currentPage - 1) * PRODUCTS_PER_PAGE
    const lastIndex = firstIndex   PRODUCTS_PER_PAGE
    const totalPages = products.length / PRODUCTS_PER_PAGE


    useEffect(() => {
        if(JSON.stringify(previousProductsRef.current) !== JSON.stringify(products) || previousPageRef.current !== currentPage) {
          const slicedProducts = products.slice(firstIndex,lastIndex);
          previousProductsRef.current = slicedProducts;
          previousPageRef.current = currentPage;
          setProducts(slicedProducts);
          console.log(slicedProducts);
       }
    }, [currentPage, products])

    return (
        <div className="products">
            {
                products.map((product) => (
                <Product product={product}/>))
            }
        </div>
    )
}
  1. Using useMemo:
const productsArray = useMemo(() => {
    const firstIndex = (currentPage - 1) * PRODUCTS_PER_PAGE;
    const lastIndex = firstIndex   PRODUCTS_PER_PAGE;

    const slicedProducts = products.slice(firstIndex, lastIndex);
    return slicedProducts;

}, [currentPage, products]);
  • Related