Reputation: 91
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
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
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