'React filtering with different urls
I am trying to filter some products by calling a different url based on what the user click in a component. I have a ProductsPage and a FilteredProductsPage component.
My scenario is this: I am on ProductsPage, I click "skin", I see all products from skin category. Now I am on FilteredProductsPage (because the url changed) and I try to click "body" and even though the url changes, the products don't re-render. I see only "skin" cateogory products. These are the Routes in App.js
<Route path='/products' element={<ProductsPage/>}/>
<Route path='/filterProducts/:category/:type' element={<FilteredProductsPage/>}/>
This is the part of code from ProductsPage component where the user click a Link (it's StyledLink but it's based on react-router-dom Link - I used styled components to style it):
<FilterDiv >
<ArrowForwardIosOutlinedIcon fontSize='small' sx={{color:'#ccc', paddingRight:'20px'}}/>
<StyledLink to='/filterProducts/skin/all'>all</StyledLink>
</FilterDiv>
<FilterDiv >
<ArrowForwardIosOutlinedIcon fontSize='small' sx={{color:'#ccc', paddingRight:'20px'}}/>
<StyledLink to='/filterProducts/body/all'>all</StyledLink>
</FilterDiv>
And this is the the whole code from the FilteredProductsPage component (ignore the suggestions part, if from a search bar component). The FilterComponent is the same as the one from ProductsPage which work both for "skin" and "body" if I click them individually
export const FilteredProductsPage = () => {
const navigate=useNavigate();
const {category}=useParams();
const {type}=useParams();
const [cartItems, setCartItems]=React.useState(0);
const [user, error] = useAuthState(auth);
const [products,setProducts]=React.useState([]);
const [filteredProducts, setFilteredProducts]=React.useState([]);
const [loading, setLoading]=React.useState(true);
const [expanded, setExpanded] = React.useState(false);
const [searchProducts, setSearchProducts]=React.useState([]);
const [value, setValue]=React.useState('');
const [suggestions, setSuggestions]=React.useState('')
const handleChange = (panel) => (event, isExpanded) => {
setExpanded(isExpanded ? panel : false);
};
const fetchProducts=async()=>{
let searchProductsClone=[];
if(category == 'skin' && type == 'all'){
let productsClone=[];
const q = query(collection(db, "products"), where("category", "==", "skin"));
const querySnapshot=await getDocs(q);
querySnapshot.forEach((doc) => {
console.log(doc.id, " => ", doc.data());
productsClone.push(doc.data())
let searchProduct={"name": doc.data().name, "brand":doc.data().brand, "img":doc.data().img[0], "uniqueName":doc.data().uniqueName}
searchProductsClone.push(searchProduct);
console.log(products);
setLoading(false);
})
setProducts(productsClone)
}
else if(category == 'body' && type == 'all'){
let productsClone=[];
const q = query(collection(db, "products"), where("category", "==", "body"));
const querySnapshot=await getDocs(q);
querySnapshot.forEach((doc) => {
console.log(doc.id, " => ", doc.data());
productsClone.push(doc.data())
let searchProduct={"name": doc.data().name, "brand":doc.data().brand, "img":doc.data().img[0], "uniqueName":doc.data().uniqueName}
searchProductsClone.push(searchProduct);
console.log(products);
setLoading(false);
})
setProducts(productsClone)
}
setSearchProducts(searchProductsClone);
}
const getSuggestions = value => {
const inputValue = value.trim().toLowerCase();
const inputLength = inputValue.length;
return inputLength === 0 ? [] : searchProducts.filter(prod =>
prod.name.toLowerCase().slice(0, inputLength) === inputValue || prod.brand.toLowerCase().slice(0, inputLength)===inputValue
);
};
const getSuggestionValue = suggestion => suggestion.brand+"-"+suggestion.name;
const renderSuggestion = suggestion => (
<div style={{display:'flex', flexDirection:'row', width:'100%'}}>
<img src={suggestion.img} style={{width:'50px', height:'50px', marginRight:'10px', marginTop:'20px'}}/>
<p>{suggestion.brand} - {suggestion.name}</p>
</div>
);
const onChange = (event, { newValue }) => {
setValue(newValue)
};
const onSuggestionsFetchRequested = ({ value }) => {
setSuggestions(getSuggestions(value))
};
const onSuggestionsClearRequested = () => {
setSuggestions(getSuggestions())
};
const inputProps = {
placeholder: 'Search for a product...',
value,
onChange: onChange
};
const onSuggestionSelected=(event, { suggestion, suggestionValue, suggestionIndex, sectionIndex, method })=>{
console.log(suggestion)
navigate('/product/'+suggestion.uniqueName)
};
const fetchCartItems=async()=>{
const q = query(collection(db, "carts"), where("userUID", "==", user?.uid));
const docRef = await getDocs(q);
if(!docRef.empty){
const currentCartItems=docRef.docs[0].data().cartItems;
setCartItems(currentCartItems);
}
}
React.useEffect(() => {
fetchCartItems();
fetchProducts();
},[], []);
return (
<div style={{display:'flex', flexDirection:'column', height:'100%', justifyContent:'center'}}>
<Navbarr items={cartItems}/>
<Container>
<Text>Discover our large selection of products or let us choose for you.</Text>
<TextSmall>Sustainably-sourced, vegan, organic and cruelty-free. Good for you and good for the planet.</TextSmall>
</Container>
<MainProductsContainer>
<FilterContainer>
{loading == true && <Loading background='white'/>}
<Autosuggest
theme={theme}
suggestions={suggestions}
onSuggestionsFetchRequested={onSuggestionsFetchRequested}
onSuggestionsClearRequested={onSuggestionsClearRequested}
onSuggestionSelected={onSuggestionSelected}
getSuggestionValue={getSuggestionValue}
renderSuggestion={renderSuggestion}
inputProps={inputProps}
/>
<h4>Categories</h4>
<Accordion expanded={expanded === 'panel1'} onChange={handleChange('panel1')} disableGutters square elevation={0}>
<AccordionSummary
expandIcon={<ExpandMoreIcon />}
aria-controls="panel1bh-content"
id="panel1bh-header"
>
<Typography sx={{ width: '33%', flexShrink: 0, fontFamily:'Montserrat', fontWeight:'600' }}>
skin
</Typography>
</AccordionSummary>
<AccordionDetails>
<FilterDiv >
<ArrowForwardIosOutlinedIcon fontSize='small' sx={{color:'#ccc', paddingRight:'20px'}}/>
<StyledLink to='/filterProducts/skin/all'>all</StyledLink>
</FilterDiv>
</AccordionDetails>
</Accordion>
<Accordion expanded={expanded === 'panel2'} onChange={handleChange('panel2')} disableGutters square elevation={0}>
<AccordionSummary
expandIcon={<ExpandMoreIcon />}
aria-controls="panel2bh-content"
id="panel2bh-header"
>
<Typography sx={{ width: '33%', flexShrink: 0, fontFamily:'Montserrat', fontWeight:'600' }}>body</Typography>
</AccordionSummary>
<AccordionDetails>
<FilterDiv >
<ArrowForwardIosOutlinedIcon fontSize='small' sx={{color:'#ccc', paddingRight:'20px'}}/>
<StyledLink to='/filterProducts/body/all'>all</StyledLink>
</FilterDiv>
</AccordionDetails>
</Accordion>
</FilterContainer>
<Products items={products}/>
</MainProductsContainer>
<Footer/>
</div>
)
}
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|
