Search code examples
reactjsreact-routerreact-router-dom

setSearchParams not deleting query param


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?


Solution

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

    useSearchParams

    Note:

    The setSearchParams function works like navigate, but only for the search portion of the URL. Also note that the second arg to setSearchParams is the same type as the second arg to navigate.

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