React useState hook 导致无限渲染

React useState hook causes infinite rendering

我正在根据搜索查询列出所有产品。我将列出的产品的所有品牌和卖家信息存储在 useState 挂钩中。直到此时它才起作用。现在我想将所有类别存储在 useState 挂钩中。

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]);

我终于渲染了 ProductFilters 组件。

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

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>

我像上面那样列出了卖家和品牌数据,它们起作用了。但是当我添加这段代码时,我得到了这个错误:

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 | 

即使我为 Categories prop 设置默认值 Categories=["a","b","c"] 我仍然会遇到同样的错误。

问题

在您的 onClick 函数中,您正在执行函数调用 setCategory("")。但是你需要的只是一个函数签名,可以在用户点击它时调用。

因此将您的 onClick 更改为内联函数。

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

如果你不使用箭头函数,setCategory函数将被一次又一次地调用。您的 ProductFilters 组件应该是这样的:

<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>