Skip to content

Commit

Permalink
fix generics of useFilter using currying
Browse files Browse the repository at this point in the history
  • Loading branch information
devgioele committed Sep 19, 2023
1 parent 4056253 commit a1f5f8d
Show file tree
Hide file tree
Showing 2 changed files with 47 additions and 44 deletions.
85 changes: 44 additions & 41 deletions src/components/util/useFilter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,49 +10,52 @@ export type FilterOptions = {
debounce?: true | number
}

export function useFilter<
TElement extends HTMLElement & { value: unknown },
TValue = TElement['value'],
>(value: TValue, setValue: (v: TValue) => void, options?: FilterOptions) {
const debounceInterval = useMemo(() => {
if (options?.debounce === true) {
return 200
export function useFilter<TElement extends HTMLElement & { value: unknown }>() {
return function useCurryingFilter<TValue extends TElement['value']>(
value: TValue,
setValue: (v: TValue) => void,
options?: FilterOptions,
) {
const debounceInterval = useMemo(() => {
if (options?.debounce === true) {
return 200
}
if (options?.debounce !== undefined && options.debounce > 0) {
return options.debounce
}
return 0
}, [options?.debounce])
const elementRef = useRef<TElement>(null)

const settingNewValueRef = useRef(false)

const [internalValue, setInternalValue] = useState(value)
const debouncedInternalValue = useDebounce(internalValue, debounceInterval)
const oldDebouncedInternalValueRef = useRef<TValue>()

useEffect(() => {
// Check that the debounced value is new, because `setValue` might not be reference stable and trigger this effect even though the debounced value did not change
if (debouncedInternalValue !== oldDebouncedInternalValueRef.current) {
oldDebouncedInternalValueRef.current = debouncedInternalValue
setValue(debouncedInternalValue)
settingNewValueRef.current = false
}
}, [debouncedInternalValue, setValue])

useEffect(() => {
if (elementRef.current && !settingNewValueRef.current) {
elementRef.current.value = value
}
}, [value])

const onChange: ChangeEventHandler<TElement> = (e) => {
settingNewValueRef.current = true
setInternalValue(e.target.value as TValue)
}
if (options?.debounce !== undefined && options.debounce > 0) {
return options.debounce
}
return 0
}, [options?.debounce])
const elementRef = useRef<TElement>(null)

const settingNewValueRef = useRef(false)

const [internalValue, setInternalValue] = useState(value)
const debouncedInternalValue = useDebounce(internalValue, debounceInterval)
const oldDebouncedInternalValueRef = useRef<TValue>()

useEffect(() => {
// Check that the debounced value is new, because `setValue` might not be reference stable and trigger this effect even though the debounced value did not change
if (debouncedInternalValue !== oldDebouncedInternalValueRef.current) {
oldDebouncedInternalValueRef.current = debouncedInternalValue
setValue(debouncedInternalValue)
settingNewValueRef.current = false
}
}, [debouncedInternalValue, setValue])

useEffect(() => {
if (elementRef.current && !settingNewValueRef.current) {
elementRef.current.value = value
return {
ref: elementRef,
onChange,
}
}, [value])

const onChange: ChangeEventHandler<TElement> = (e) => {
settingNewValueRef.current = true
setInternalValue(e.target.value as TValue)
}

return {
ref: elementRef,
onChange,
}
}
6 changes: 3 additions & 3 deletions src/examples/List.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ export const ListWithFilter: Story = () => {
(filter.role === '' || item.role === filter.role) &&
(filter.department === '' || item.department === filter.department),
)
const searchFilterProps = useFilter<HTMLInputElement>(
const searchFilterProps = useFilter<HTMLInputElement>()(
filter.search,
(v) => {
setFilter((prevFilter) => ({ ...prevFilter, search: v }))
Expand All @@ -125,10 +125,10 @@ export const ListWithFilter: Story = () => {
debounce: true,
},
)
const roleFilterProps = useFilter<HTMLSelectElement>(filter.role, (v) => {
const roleFilterProps = useFilter<HTMLSelectElement>()(filter.role, (v) => {
setFilter((prevFilter) => ({ ...prevFilter, role: v }))
})
const departmentFilterProps = useFilter<HTMLSelectElement>(
const departmentFilterProps = useFilter<HTMLSelectElement>()(
filter.department,
(v) => {
setFilter((prevFilter) => ({ ...prevFilter, department: v }))
Expand Down

0 comments on commit a1f5f8d

Please sign in to comment.