useProductFilters

 avatar
unknown
typescript
a year ago
1.8 kB
7
Indexable
import { usePathname, useRouter } from "next/navigation"
import { useCallback, useEffect, useMemo, useState } from "react"

interface ProductFilters {
  search: string | null
  category: string | null
  rating: [number, number] | null
  maxPrice: number | null
}

type FilterKey = keyof ProductFilters
type FilterValue = string | number | [number, number] | null

const initFilters: ProductFilters = {
  search: null,
  category: null,
  rating: null,
  maxPrice: null,
}

const parseFilterValue = (value: FilterValue): string | null => {
  if (value === null) {
    return null
  }

  if (typeof value === "string") {
    return value
  }

  if (typeof value === "number") {
    return value.toString()
  }

  if (
    Array.isArray(value) &&
    value.length === 2 &&
    typeof value[0] === "number" &&
    typeof value[1] === "number"
  ) {
    return `${value[0]} - ${value[1]}`
  }

  return null
}

export default function useProductFilters() {
  const router = useRouter()
  const pathname = usePathname()

  const [filters, setFilters] = useState<ProductFilters>(initFilters)
  const params = useMemo(() => {
    const params = new URLSearchParams()

    for (const key of Object.keys(filters)) {
      const value = parseFilterValue(filters[key as FilterKey])

      if (!!value) {
        params.set(key, value)
      } else {
        params.delete(key)
      }
    }

    return params
  }, [filters])

  const setFilter = <K extends FilterKey>(
    key: K,
    value: ProductFilters[K]
  ): void => {
    setFilters((filters) => ({ ...filters, [key]: value }))
  }

  const getFilter = useCallback(
    <K extends FilterKey>(key: K): ProductFilters[K] => {
      return filters[key]
    },
    [filters]
  )

  return { setFilter, getFilter }
}
Editor is loading...
Leave a Comment