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.