I have a SearchBar
component where I have this Link
which on click takes me to a ProductSearchScreen
which display a set of products associated with a category.
return (
<Link to={{ pathname:# somepath, search: `searchItem=${category.name}` }}>
{category.name}
</Link>
)
In my ProductSearchScreen
, I have checkboxes which filter out the data such as "category", "brand" filter etc...
For filtering out categories for example, I have this code
const [selectedCategories, setSelectedCategories] = useState(queryParams.get('categories')?.split(',') ?? []);
<input
type="checkbox"
checked={selectedCategories.includes(category)}
onClick={() => handleCategories(category)}
/>
The issue is, suppose I'm in ProductSearchScreen
of category1
and have few checkboxes clicked and in searchBar I type category2
and navigate to category2
page, the checkboxes still remain checked
for category2
.
Though I don't have anything in queryParams
on navigate, it is not resetting the state of selectedCategories
.
How can I make sure the checkboxes are not checked on navigating to another page?
Edit: This is the component consuming the path and querystring
function ProductSearchScreen() {
const [products, setProducts] = useState([]);
const [queryParams] = useSearchParams();
// Here is the queryString i am getting
const category = queryParams.get('searchItem');
const categories = queryParams.get('categories')?.split(',');
const uniqueCategories = getUniqueCategories(products)
// with axios call, i get the list of products and update the state like setProducts(products).
// This is a helper function to filter the categories
const filterCategories = (product) => {
if(categories?.length > 0) {
return categories?.includes(product.category);
} else {
return product
}
}
const filteredAndSortedProducts = products?.filter(filterCategories)
return ( <Filters uniqueCategories={uniqueCategories}/> )
}
function Filters({ uniqueCategories }) {
const [queryParams, setQueryParams] = useSearchParams();
const [selectedCategories, setSelectedCategories] = useState(queryParams.get('categories')?.split(',') ?? []);
const handleCategories = (category) => {
if (selectedCategories.includes(category)) {
setSelectedCategories(selectedCategories.filter((c) => c !== category));
} else {
setSelectedCategories([...selectedCategories, category]);
}
}
return (
<div key={index} className="filter-type">
<input
type="checkbox"
disabled={queryParams.get('categories')?.split(',').includes(category)}
checked={selectedCategories.includes(category)}
onChange={() => handleCategories(category)}
/>
<div className="filter-item">{category}</div>
</div>
)
}
The Filters
component only checks the query params once when it mounts. If it needs to react to changes in the "categories"
query params then it should also implement a lifecycle hook, e.g. useEffect
, to update the selectedCategories
.
function Filters({ uniqueCategories }) {
const [queryParams, setQueryParams] = useSearchParams();
const categories = queryParams.get('categories');
const [selectedCategories, setSelectedCategories] = useState(categories?.split(',') ?? []);
useEffect(() => {
setSelectedCategories(categories?.split(',') ?? []);
}, [categories]);
...
return (
...
);
}
Note that when you see that you've written a useState
/useEffect
combo that you may actually just need the useMemo
hook.
function Filters({ uniqueCategories }) {
const [queryParams, setQueryParams] = useSearchParams();
const categories = queryParams.get('categories');
const selectedCategories = useMemo(() => {
return categories?.split(',') ?? [];
}, [categories]);
...
return (
...
);
}