Güney
Güney

Reputation: 91

React useState hook causes infinite rendering

I'm listing all products according to the search query. I store all brands and sellers information of listed products in useState hook. Which until this point it works. Now I want to store all categories in useState hook.

listProducts are the listed version of results. Thoose results can be sorted or filtered seperately by user. And defaultProducts will not be effected. defaultProducts is what I get from the database.

  const { query } = useParams();
  const [products, setProducts] = useState([]);
  const [listProducts, setListProducts] = useState([]);
  const [brands, setBrands] = useState([]);
  const [sellers, SetSellers] = useState([]);
  const [Categories, setCategories] = useState([]);
  const [isLoading, setIsLoading] = useState(false);

  useEffect(() => {
    setIsLoading(true);
    
    axios
      .get(`/api/product/search/${query}`)
      .then((res) => res.data)
      .then((data) => {
        setIsLoading(false);
        setProducts(data.products);
        setListProducts(data.products);

        if (data.products.length < 1) {
          setProducts(data.brands);
          setListProducts(data.brands);
        }
      });
  }, [query]);

  useEffect(() => {
    let brandSet = new Set();
    let sellerSet = new Set();
    let categoriesSet = new Set();

    products.forEach((item) => {
      categoriesSet.add(item.category);
      categoriesSet.add(item.subCategory);
      brandSet.add(item.brand);
      sellerSet.add(item.shop.companyName);
    });

    setCategories([...categoriesSet]);
    setBrands([...brandSet]);
    SetSellers([...sellerSet]);
  }, [products]);

And I finally render ProductFilters component.

 <ProductFilters
   DefaultProducts={products}
   ListProducts={listProducts}
   Brands={brands}
   Sellers={sellers}
   Categories={Categories}
   setListProducts={setListProducts}
   Brand={brand}
 />

In ProductFilters.js

<FilterTypeSection className="mt-3">
        <FilterType>Categories</FilterType>
        <CategoriesSection>
          {Categories.map((categoryItem, index) => {
            return (
              <CategoryItem
                key={index}
                onClick={
                  category === categoryItem
                    ? setCategory("")
                    : setCategory(categoryItem)
                }
                style={
                  categoryItem === category
                    ? { fontWeight: "bold", color: "black" }
                    : { fontWeight: "normal", color: "var(--text-muted)" }
                }
              >
                {categoryItem}
              </CategoryItem>
            );
          })}
        </CategoriesSection>
</FilterTypeSection>

I listed sellers and brands data like above and they worked. But when I added this code I got this error:

Unhandled Rejection (Error): Too many re-renders. React limits the number of renders to prevent an infinite loop.
▶ 18 stack frames were collapsed.
(anonymous function)
src/pages/searchResultsPage.js:162
  159 |   sellerSet.add(item.shop.companyName);
  160 | });
  161 | 
> 162 | setCategories([...categoriesSet]);
      | ^  163 | setBrands([...brandSet]);
  164 | SetSellers([...sellerSet]);
  165 | 

Even if I set a default value for Categories prop like Categories=["a","b","c"] I still get same error.

Upvotes: 0

Views: 459

Answers (2)

Prayag Choraria
Prayag Choraria

Reputation: 819

The setCategory function will be called again and again if you don't use arrow function there. Your ProductFilters component should be like this:

<FilterTypeSection className="mt-3">
    <FilterType>Categories</FilterType>
    <CategoriesSection>
        {Categories.map((categoryItem, index) => {
            return (
              <CategoryItem
                key={index}
                onClick={
                  () => category === categoryItem
                    ? setCategory("")
                    : setCategory(categoryItem)
                }
                style={
                  categoryItem === category
                    ? { fontWeight: "bold", color: "black" }
                    : { fontWeight: "normal", color: "var(--text-muted)" }
                }
              >
                {categoryItem}
              </CategoryItem>
            );
        })}
    </CategoriesSection>
</FilterTypeSection>

Upvotes: 1

Shyam
Shyam

Reputation: 5497

Issue

In your onClick function you are doing a function call setCategory("") . But what you need is a just a function signature which can be called at a later time when the user clicks it .

so change your onClick as an inline function.

onClick={() => {
  category === categoryItem
  ? setCategory("")
  : setCategory(categoryItem)
}

Upvotes: 3

Related Questions