I have 4 buttons that I'm rendering on my UI
const categories = ['Thing1', 'Thing2', 'Thing3', 'Thing4'];
// ...
const [filters, setFilters] = useState<string[]>([]);
const handleCategoryClick = useCallback((category: string) => {
const updatedFilters = [...filters];
// Deleting an existing filter
// Find the index of the existing filter and remove it
const index = updatedFilters.indexOf(value);
if (index > -1) {
updatedFilters.splice(index, 1);
setFilters(updatedFilters);
return;
}
// Adding a filter
updatedFilters.push(value);
setFilters(updatedFilters)
}, [filters);
return (
<Container>
{categories.map(category => (
<div onClick={() => handleCategoryClick(category)}>{category}</div>
)}
</Container>
);
This works well, but now I want to add query params with react-router
and useSearchParams
.
I am able to add another useEffect
and add/remove params to my URL with useSearchParams
, however I want to remove the query param when there are no categories selected, which is not working:
useEffect(() => {
if (!!filters.length) {
setSearchParams({
categories: filters.map((category) => category.toLowerCase()).join(','),
});
} else if (!filters.length) {
if (searchParams.has('categories')) {
console.log('search param exists');
searchParams.delete('categories');
}
}
}, [filters]);
searchParams.delete('categories')
isn't exactly working and I'm not sure why -- my console.log
is logging something, but deleting it doesn't work. As a result, I'm left with a url that looks like localhost:3000/classes?categories=Thing1
even if there's no existing filters (empty arr).
Am I doing something wrong with trying to delete the param? Should I fall back to URLSearchParam
?
searchParams
is just the URLSearchParams
object, mutating it in code only mutates the object and doesn't do anything to the current URL. For this you must call setSearchParams
again and pass the updated searchParams
object to effect a navigation change with the new queryString value.
Note:
The
setSearchParams
function works likenavigate
, but only for the search portion of the URL. Also note that the second arg tosetSearchParams
is the same type as the second arg tonavigate
.
useEffect(() => {
if (!!filters.length) {
setSearchParams({
categories: filters.map((category) => category.toLowerCase()).join(','),
});
} else {
if (searchParams.has('categories')) {
console.log('search param exists');
searchParams.delete('categories');
setSearchParams(searchParams);
}
}
}, [filters, searchParams]);
There is also a callback updater syntax (similar how React.useState
works) that is passed the current URLSearchParams
value.
useEffect(() => {
if (!!filters.length) {
setSearchParams({
categories: filters.map((category) => category.toLowerCase()).join(','),
});
} else {
setSearchParams(searchParams => {
if (searchParams.has('categories')) {
console.log('search param exists');
searchParams.delete('categories');
}
return searchParams;
});
}
}, [filters]);