Skip to content

Commit 2d5cdad

Browse files
committed
Add a custom react hook to enable fuzzy searching in item lists
1 parent 64ca9dc commit 2d5cdad

1 file changed

Lines changed: 61 additions & 0 deletions

File tree

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
import { useMemo, useState } from "react";
2+
import { matchSorter } from "match-sorter";
3+
4+
/**
5+
* A hook that provides fuzzy filtering functionality for a list of objects.
6+
* Uses match-sorter to perform the filtering across multiple object properties and
7+
* consistently order the results by score.
8+
*
9+
* @param params - The parameters object
10+
* @param params.items - Array of objects to filter
11+
* @param params.keys - Array of object keys to perform the fuzzy search on
12+
* @returns An object containing:
13+
* - filterText: The current filter text
14+
* - setFilterText: Function to update the filter text
15+
* - filteredItems: The filtered array of items based on the current filter text
16+
*
17+
* @example
18+
* ```tsx
19+
* const users = [{ name: "John", email: "john@example.com" }];
20+
* const { filterText, setFilterText, filteredItems } = useFuzzyFilter({
21+
* items: users,
22+
* keys: ["name", "email"]
23+
* });
24+
* ```
25+
*/
26+
export function useFuzzyFilter<T extends Object>({
27+
items,
28+
keys,
29+
}: {
30+
items: T[];
31+
keys: Extract<keyof T, string>[];
32+
}) {
33+
const [filterText, setFilterText] = useState("");
34+
35+
const filteredItems = useMemo<T[]>(() => {
36+
const filterTerms = filterText
37+
.trim()
38+
.split(" ")
39+
.map((term) => term.trim())
40+
.filter((term) => term !== "");
41+
42+
if (filterTerms.length === 0) {
43+
return items;
44+
}
45+
46+
// sort by the score of the first term
47+
return filterTerms.reduceRight(
48+
(results, term) =>
49+
matchSorter(results, term, {
50+
keys,
51+
}),
52+
items
53+
);
54+
}, [items, filterText]);
55+
56+
return {
57+
filterText,
58+
setFilterText,
59+
filteredItems,
60+
};
61+
}

0 commit comments

Comments
 (0)