How correct organize invoking new request with updated query string in RTK query by click button in inner component?

48 Views Asked by At

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

0

There are 0 best solutions below