Last active
October 29, 2021 22:31
-
-
Save gengue/20dc3a80d808ee908380488ce673542c to your computer and use it in GitHub Desktop.
React hook to filter a list using local state and Fuse.js Demo: https://codesandbox.io/s/tender-stallman-qi1gl?fontsize=14
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 React, { useState } from "react"; | |
import ReactDOM from "react-dom"; | |
import useFuzzySearch from "./useFuzzySearch"; | |
const persons = [ | |
{ id: 1, name: "genesis" }, | |
{ id: 2, name: "jose" }, | |
{ id: 3, name: "mauro" }, | |
{ id: 4, name: "fredo" }, | |
{ id: 5, name: "meggie" }, | |
{ id: 6, name: "gustavo 527" }, | |
{ id: 7, name: "santiago" }, | |
]; | |
function FuzzySearchDemo() { | |
const { list, onSearch } = useFuzzySearch(persons, ["name"]); | |
return ( | |
<div className="App"> | |
<h1>Fuzzy Search</h1> | |
<input name="search_fuzzy" onChange={onSearch} /> | |
<ul> | |
{list.map(i => ( | |
<li key={i.id}>{i.name}</li> | |
))} | |
</ul> | |
</div> | |
); | |
} | |
const rootElement = document.getElementById("root"); | |
ReactDOM.render(<FuzzySearchDemo />, rootElement); |
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 { useEffect, useState, useMemo, useRef } from 'react'; | |
import useDebounce from './useDebounce'; | |
import Fuse from 'fuse.js'; | |
/** | |
* @const | |
*/ | |
const DEFAULT_FUSE_OPTIONS = { | |
shouldSort: true, | |
location: 0, | |
distance: 100, | |
threshold: 0.6, | |
}; | |
/* fuse instance */ | |
let fuse; | |
/** | |
* useFuzzySearch | |
* @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 useFuzzySearch(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 = e => { | |
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 { list, onSearch }; | |
} | |
export default useFuzzySearch; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment