1

I am building a simple paginated list of entities with a support for dynamic filters with ReactJS. The data is fetched with the help of react-query library from a RESTful API layer. It is paginated and filtered on the server. Selected filters need to be stored in a query parameter of the URL, such that they are restored upon refreshing the page. I am struggling to find the cleanest way of synchronizing the state of the filters with the current URL. I don't want to introduce additional dependencies such as React Router, but I am already using redux in the app. However, I don't want to replicate the URL state in the redux store.

The components structure with their props are as follows (simplified for the purpose of the question):

FilterBar

This component renders filters in the UI, that are provided with the dropdownFilters prop. It supports multi-select for each filter. It takes the following props:

selectedFiltersIds: { [k: string]: string[] };
setSelectedFiltersIds: (selected: { [k: string]: string[] }) => void;
dropdownFilters: FilterDropdownType[];

Example of the selectedFiltersIds, with two pre-selected filters:

{
 locations: [1,4,5],
 zones: [3,2]
}

Another component, List fetches the list data:

export const List: React.FC = ({ selectedFiltersIds }) => {
  const listData = useListData(page, { ...selectedFiltersIds });
  return <>{listData.data.items.map((i, index) => (
      <div key={i.id}>
        <Card {...i} />
      </div>
    ))}</>
}

The ListParent:

const ListParent: React.FC = () => {
  const filtersData = useFilters();
  
  const dropdownFilters = useMemo(() => {
      return filtersData?.data?.groups
        .map((f: any) => ({
          ...f,
          items: f.items.map((i: any) => ({ name: i.name, val: i })),
        }));
  }, [brandFiltersData]);
  
  const [selectedFiltersIds, setSelectedFiltersIds] = useState<{
    [k: string]: string[];
  }>({}); // this needs to be synchronized with the query params in the browser's URL
  
  if (filtersData.isLoading) {
    return <LoadingScreen />;
  }
  
  return (
    <main>
      <FilterBar
        selectedFiltersIds={selectedFiltersIds}
        setSelectedFiltersIds={setSelectedFiltersIds}
        dropdownFilters={dropdownFilters}
      />
      <List selectedFiltersIds={selectedFiltersIds} />
    </main>
  );
};

export default ListParent;

I am not sure what would be the best way to move the selected filters from the internal state of the ListParent and synchronize it with the URL state. I don't want to maintain two source of truths for this particular case.

ers36
  • 181
  • 5

1 Answers1

0

It seems that the use-query-params library provides a simple and clean way of solving this issue. It supports two-way-binding of query params without the need of React Router (example repo) or redux.

ers36
  • 181
  • 5