Search code examples
javascriptreactjsreact-hooksreact-router-dom

Unable to reset the state using react-router-dom Link


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>
    )
}

Solution

  • 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 (
        ...
      );
    }