Last active
June 16, 2020 14:03
-
-
Save luizcieslak/3426858cc837eae36495177b5efab986 to your computer and use it in GitHub Desktop.
useFuseSearch + useDebounce (TS and JS version) https://codesandbox.io/s/usefusesearch-ts-h8ueb
This file contains hidden or 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
import { useState, useEffect } from 'react'; | |
function useDebounce(value, delay) { | |
// State and setters for debounced value | |
const [debouncedValue, setDebouncedValue] = useState(value); | |
useEffect( | |
() => { | |
// Set debouncedValue to value (passed in) after the specified delay | |
const handler = setTimeout(() => { | |
setDebouncedValue(value); | |
}, delay); | |
// Return a cleanup function that will be called every time ... | |
// ... useEffect is re-called. useEffect will only be re-called ... | |
// ... if value changes (see the inputs array below). | |
// This is how we prevent debouncedValue from changing if value is ... | |
// ... changed within the delay period. Timeout gets cleared and restarted. | |
// To put it in context, if the user is typing within our app's ... | |
// ... search box, we don't want the debouncedValue to update until ... | |
// ... they've stopped typing for more than 500ms. | |
return () => { | |
clearTimeout(handler); | |
}; | |
}, | |
// Only re-call effect if value changes | |
// You could also add the "delay" var to inputs array if you ... | |
// ... need to be able to change that dynamically. | |
[value, delay] | |
); | |
return debouncedValue; | |
} | |
export default useDebounce; |
This file contains hidden or 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
import { useState, useEffect } from "react"; | |
function useDebounce(value: string, delay: number) { | |
// State and setters for debounced value | |
const [debouncedValue, setDebouncedValue] = useState(value); | |
useEffect(() => { | |
// Set debouncedValue to value (passed in) after the specified delay | |
const handler = setTimeout(() => { | |
setDebouncedValue(value); | |
}, delay); | |
// Return a cleanup function that will be called every time ... | |
// ... useEffect is re-called. useEffect will only be re-called ... | |
// ... if value changes (see the inputs array below). | |
// This is how we prevent debouncedValue from changing if value is ... | |
// ... changed within the delay period. Timeout gets cleared and restarted. | |
// To put it in context, if the user is typing within our app's ... | |
// ... search box, we don't want the debouncedValue to update until ... | |
// ... they've stopped typing for more than 500ms. | |
return () => { | |
clearTimeout(handler); | |
}; | |
}, // You could also add the "delay" var to inputs array if you ... // Only re-call effect if value changes | |
// ... need to be able to change that dynamically. | |
[value, delay]); | |
return debouncedValue; | |
} | |
export default useDebounce; |
This file contains hidden or 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
//equals to https://gist.github.com/gengue/20dc3a80d808ee908380488ce673542c | |
// w/ caseSensitive option | |
import { useEffect, useState, useMemo, useRef } from 'react' | |
import useDebounce from './useDebounce' | |
import Fuse from 'fuse.js' | |
/** | |
* @const | |
*/ | |
const DEFAULT_FUSE_OPTIONS = { | |
shouldSort: false, | |
location: 0, | |
distance: 100, | |
threshold: 0.6, | |
caseSensitive: false | |
} | |
/* fuse instance */ | |
let fuse | |
/** | |
* useFuseSearch | |
* @example | |
* const { list, onSearch } = useFuzzySearch(apiList, [ 'first_name', 'last_name' ]); | |
* @param {array} originalList=[] | |
* @param {array} keys=[] | |
* @param {number} ms=500 | |
* @returns {object} = list<Array>, onSearch<function> | |
*/ | |
function useFuseSearch(originalList = [], keys = [], ms = 500) { | |
const source = useRef(originalList) | |
const [list, setList] = useState(originalList) | |
const [rawSearch, setSearch] = useState(null) | |
// the hook will only return the latest value (what we passed in) | |
const search = useDebounce(rawSearch, ms) | |
const fuseOptions = useMemo(() => ({ ...DEFAULT_FUSE_OPTIONS, keys }), [keys]) | |
const onSearch = value => { | |
// const value = e.target ? e.target.value : e.q; | |
setSearch(value) | |
} | |
useEffect(() => { | |
if (!source.current || source.current.length === 0) { | |
setList(originalList) | |
} | |
fuse = new Fuse(originalList, fuseOptions) | |
source.current = originalList | |
}, [fuseOptions, originalList]) | |
useEffect(() => { | |
function handleSearch(text) { | |
// null means we have to show the original list, not the filtered one | |
let result = null | |
if (text && text !== '') { | |
result = fuse.search(text) | |
} | |
setList(result === null ? source.current : result) | |
} | |
if (search !== null) { | |
handleSearch(search) | |
} | |
}, [search]) | |
return { results: list, onSearch } | |
} | |
export default useFuseSearch |
This file contains hidden or 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
//based on https://gist.github.com/gengue/20dc3a80d808ee908380488ce673542c | |
import { useEffect, useState, useMemo, useRef } from "react"; | |
import useDebounce from "./useDebounce"; | |
import Fuse, { FuseResult } from "fuse.js"; | |
/** | |
* @const | |
* @see: https://fusejs.io/api/options.html | |
*/ | |
const DEFAULT_FUSE_OPTIONS = { | |
shouldSort: false, | |
location: 0, | |
distance: 100, | |
threshold: 0.2, | |
caseSensitive: false | |
}; | |
/** | |
* useFuseSearch | |
* @example | |
* const { list, onSearch } = useFuseSearch(apiList, [ 'first_name', 'last_name' ]); | |
* @param {array} originalList=[] | |
* @param {array} keys=[] | |
* @param {number} ms=500 | |
* @returns {object} = results<Array>, onSearch<function> | |
*/ | |
function useFuseSearch<T, K extends keyof T>( | |
originalList: T[] = [], | |
keys: K[] = [], | |
ms = 600 | |
) { | |
// Comment the following line if you want to return an empty list when there is no results. | |
const source = useRef(originalList); | |
const [list, setList] = useState(originalList); | |
const [rawSearch, setSearch] = useState(""); | |
// the hook will only return the latest value (what we passed in) | |
const search = useDebounce(rawSearch, ms); | |
const fuseOptions = useMemo( | |
() => ({ ...DEFAULT_FUSE_OPTIONS, keys: keys as string[] }), | |
[keys] | |
); | |
/* fuse instance */ | |
let fuse = useMemo(() => new Fuse(originalList, fuseOptions), [ | |
originalList, | |
fuseOptions | |
]); | |
/** | |
* onSearch function to be added directly to input element. | |
* @example <input onChange={onSearch} /> | |
* @param e React.ChangeEvent<HTMLInputElement> | |
* | |
* change this if you want to pass a string to useFuseSearch | |
*/ | |
const onSearch = (e: React.ChangeEvent<HTMLInputElement>) => { | |
setSearch(e.target.value); | |
}; | |
useEffect(() => { | |
source.current = originalList; | |
}, [originalList]); | |
useEffect(() => { | |
function handleSearch(text: any) { | |
let result: FuseResult<T>[] = []; | |
if (text && text !== "") { | |
result = fuse.search(text); | |
} | |
// if results is empty, return original list | |
// remove this if you want to return a message for empty results. | |
setList(result.length === 0 ? source.current : result); | |
} | |
if (search !== null) { | |
handleSearch(search); | |
} | |
// Warning: puttingg fuse in array of deps gives an infinite loop. | |
}, [search, rawSearch]); | |
return { results: list, onSearch }; | |
} | |
export default useFuseSearch; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment