-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: filter by search input and sync with url params
- Loading branch information
Showing
7 changed files
with
291 additions
and
26 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
import { useMemo } from 'react' | ||
import { useAppState } from '@/contexts/appState' | ||
import { FilterAddAction, FilterDelAction } from '@/store/filter' | ||
|
||
export type UseFilterProps = { | ||
key: string | ||
debounced?: number | ||
} | ||
|
||
export type UseFilterReturn = [string | undefined, (value: string) => void] | ||
|
||
// @todo: move it to core package utils | ||
export function debounce( | ||
cb: (...args: any[]) => void, | ||
delay: number, | ||
): (...args: any) => void { | ||
let id: NodeJS.Timeout | ||
|
||
return (args) => { | ||
clearTimeout(id) | ||
id = setTimeout(() => cb(args), delay) | ||
} | ||
} | ||
|
||
export function useFilter({ | ||
key, | ||
debounced = 0, | ||
}: UseFilterProps): UseFilterReturn { | ||
const [state, dispatch] = useAppState() | ||
const filters = state.filter | ||
|
||
const filter = filters[key]?.value | ||
|
||
const handleChange = useMemo(() => { | ||
return debounce((value) => { | ||
return value | ||
? dispatch(new FilterAddAction({ key, value })) | ||
: dispatch(new FilterDelAction({ key })) | ||
}, debounced) | ||
}, [debounced, key, dispatch]) | ||
|
||
return [filter, handleChange] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,109 @@ | ||
import { useCallback, useEffect } from 'react' | ||
import { useRouter } from 'next/router' | ||
import { useAppState } from '@/contexts/appState' | ||
import { | ||
FilterAddAction, | ||
FilterDelAction, | ||
FilterItem, | ||
FilterSetAction, | ||
} from '@/store/filter' | ||
|
||
export type UseFiltersProps = { | ||
syncUrl?: boolean | ||
} | ||
|
||
export type UseFiltersReturn = { | ||
filters: Record<string, FilterItem | null> | ||
handleAddFilter: (key: string, value: string) => void | ||
handleDelFilter: (key: string) => void | ||
} | ||
|
||
export function useFilters({ | ||
syncUrl = false, | ||
}: UseFiltersProps): UseFiltersReturn { | ||
const [state, dispatch] = useAppState() | ||
const filters = state.filter | ||
|
||
const router = useRouter() | ||
|
||
// ----------------------------- | ||
|
||
useEffect(() => { | ||
if (!syncUrl) return | ||
if (!router.isReady) return | ||
|
||
const urlArgs = router.asPath.split('?')[1] | ||
const urlParams = Object.fromEntries(new URLSearchParams(urlArgs).entries()) | ||
const stateParams = Object.fromEntries( | ||
Object.entries(filters).map(([key, val]) => [key, val ? val.value : val]), | ||
) | ||
|
||
const newFilters: Record<string, string | null> = { | ||
...urlParams, | ||
...stateParams, | ||
} | ||
|
||
const addToState = Object.keys(newFilters).filter((key) => { | ||
const fValue = stateParams[key] || null | ||
return fValue !== newFilters[key] | ||
}) | ||
|
||
const addToUrl = Object.keys(newFilters).filter((key) => { | ||
const qValue = urlParams[key] || null | ||
return qValue !== newFilters[key] | ||
}) | ||
|
||
if (addToState.length) { | ||
const state = addToState.reduce( | ||
(ac, key) => { | ||
const value = newFilters[key] | ||
ac[key] = value ? { key, value } : null | ||
return ac | ||
}, | ||
{ ...filters }, | ||
) | ||
|
||
console.log('SYNC FILTER STATE', state) | ||
|
||
dispatch(new FilterSetAction({ state })) | ||
} | ||
|
||
if (addToUrl.length) { | ||
const query = addToUrl.reduce( | ||
(ac, key) => { | ||
const value = newFilters[key] | ||
|
||
if (value) { | ||
ac[key] = value | ||
} else { | ||
delete ac[key] | ||
} | ||
|
||
return ac | ||
}, | ||
{ ...router.query }, | ||
) | ||
|
||
console.log('SYNC FILTER QUERY', query) | ||
|
||
router.push({ query }) | ||
} | ||
}, [syncUrl, filters, router, dispatch]) | ||
|
||
const handleAddFilter = useCallback( | ||
(key: string, value: string) => | ||
dispatch(new FilterAddAction({ key, value })), | ||
[dispatch], | ||
) | ||
|
||
const handleDelFilter = useCallback( | ||
(key: string) => dispatch(new FilterDelAction({ key })), | ||
[dispatch], | ||
) | ||
|
||
return { | ||
filters, | ||
handleAddFilter, | ||
handleDelFilter, | ||
} | ||
} |
Oops, something went wrong.