The page contains a list of products and a list of filters. I get query params from url from which I request a list of products. In addition, FilterList requests a list of attributes of products in this category to create filters, which should change the query string in the url and, using a button, request a new list of products via an updated searchParams. The FilterList component should have a form inside with checkboxes for each attribute, and a button when clicked on which, the state of the selected attributes, should transform searchParams and somehow invoke getting a new products list with the selected parameters. Moreover, if the user just opened page via a link with searchParams in the URL, then the FilterList should transform them into the state of the form for checkboxes. This is what I have at the moment.
Product page
export const Products: React.FC = (props) => {
const [searchParams, setSearchParams] = useSearchParams();
// mapping searchParams cuz it cannot return a ready-made querries object
const mapQueries = (searchParams: URLSearchParams) => {
const queries: any = {};
for (let entry of searchParams.entries()) {
queries[entry[0]] = entry[1];
}
if (queries['page'] === undefined) {
queries['page'] = '1';
}
if (queries['cat_id'] === undefined) {
queries['cat_id'] = '1';
}
return queries;
};
const [queries, setQueries] = useState<ISearchParams>(
mapQueries(searchParams),
);
const filterHandler = (queries: ISearchParams) =>
setQueries((prev) => ({ ...queries }));
const { data, isLoading, error } = useGetProductsByCatIdQuery(queries);
return (
<AppLayout>
<Box
component="main"
sx={{ width: { sm: `calc(100% - ${240}px)` } }}
>
<ProductsList products={data} isLoading={isLoading} />
<FilterList doFilter={filterHandler} />
</Box>
</AppLayout>
);
};
FilterList component
interface ProductFilterProps {
doFilter: (queries: ISearchParams) => void;
}
interface IFilterFormState {
[key: string]: {
[key: string]: boolean;
};
}
export const FilterList: React.FC<ProductFilterProps> = (props) => {
const [searchParams, setSearchParams] = useSearchParams();
const [filterOpen, setFilterOpen] = useState<boolean>(false);
const { data, isLoading, error } = useGetCategoryByIdQuery(
Number(searchParams?.get('cat_id')),
);
// mapping list of Attributes to checkboxes state
const mapAttributesToFilterState = (
attributes: any[],
searchParams: URLSearchParams,
): IFilterFormState => {
const state: IFilterFormState = {};
for (let i = 0; i < attributes.length; i++) {
const attr_alias = attributes[i].alias;
state[attr_alias] = {};
if (attributes[i].options?.length) {
for (let j = 0; j < attributes[i].options.length; j++) {
const option_alias = attributes[i].options[j].alias;
state[attr_alias][option_alias] = false;
}
}
}
return state;
};
const filterOpenHandler = () => {
setFilterOpen(!filterOpen);
};
const formMethods = useForm<IFilterFormState>({
defaultValues: {},
});
const { control, handleSubmit, reset } = formMethods;
useEffect(() => {
if (!isLoading) {
const initialFilterState = mapAttributesToFilterState(
data.attributes,
searchParams,
);
reset(initialFilterState);
}
}, [isLoading]);
const submit = (filterState: IFilterFormState) => {
/**
* 1) transform filterState to queries object;
* 2) update searchParams
* 3) update queries state for invoking new request products
*
* const updatedQueries: ISearchParams = { };
setSearchParams(updatedQueries);
props.doFilter(updatedQueries);
*/
};
return (
<Box sx={{ width: { sm: 240 }, flexShrink: { sm: 0 } }}>
<FilterDrawer
drawerWidth={240}
filterOpen={filterOpen}
handleFilterClose={filterOpenHandler}
>
<div>
{isLoading ? (
<div>...Loading</div>
) : (
<FormProvider {...formMethods}>
<div>
{data.name}
<div className="property-list">
{data.attributes.map((attr: any) => {
return (
<FilterPropertyItem key={attr.id} attribute={attr} />
);
})}
</div>
<button onClick={handleSubmit(submit)}>Filter</button>
</div>
</FormProvider>
)}
</div>
</FilterDrawer>
</Box>
);
};
FilterPropertyItem
const FilterPropertyItem: React.FC<FilterPropertyItemProps> = ({
attribute,
}) => {
const { control } = useFormContext();
return (
<div className="property" key={attribute.id}>
{attribute.name}
{Array.isArray(attribute.options) ? (
<div>
{attribute.options.map((option: any) => (
<Controller
name={`[${attribute.alias}][${option.alias}]`}
control={control}
render={({ field: { value, onChange } }) => (
<div className="option" key={option.id}>
<input type="checkbox" onChange={onChange} checked={value} />
<span>{option.value}</span>
<span>({option.quantity})</span>
</div>
)}
/>
))}
</div>
) : null}
</div>
);
};