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.
