Skip to main content

SearchFilter

SearchFilter.tsx
import React, { useState, useEffect, useCallback } from 'react';
import './SearchFilter.css';

type SearchFilterProps<T> = {
items: T[];
filterKey: keyof T;
onFilter: (filteredItems: T[]) => void;
}

const SearchFilter = <T extends Record<string, any>>(props: SearchFilterProps<T>) => {
const { items, filterKey, onFilter } = props;
const [searchTerm, setSearchTerm] = useState<string>('');

const handleSearchTermChange = useCallback((event: React.ChangeEvent<HTMLInputElement>) => {
setSearchTerm(event.target.value);
}, []);

useEffect(() => {
const filteredItems = items.filter(item => {
const itemValue = item[filterKey].toString().toLowerCase();
const searchValue = searchTerm.toLowerCase();
return itemValue.includes(searchValue);
});
onFilter(filteredItems);
}, [items, filterKey, searchTerm, onFilter]);

return (
<div className="search-filter">
<input
type="text"
placeholder="Search"
value={searchTerm}
onChange={handleSearchTermChange}
className="search-filter__input"
/>
</div>
);
}

export default SearchFilter;


Usage
import React, { useState } from 'react';
import SearchFilter from './SearchFilter';
import './App.css';

type Item = {
id: number;
name: string;
}

const items: Item[] = [
{ id: 1, name: 'Apple' },
{ id: 2, name: 'Banana' },
{ id: 3, name: 'Cherry' },
{ id: 4, name: 'Durian' },
{ id: 5, name: 'Elderberry' },
];

const App = () => {
const [filteredItems, setFilteredItems] = useState<Item[]>(items);

const handleFilter = (filteredItems: Item[]) => {
setFilteredItems(filteredItems);
}

return (
<div className="app">
<SearchFilter items={items} filterKey="name" onFilter={handleFilter} />
<ul>
{filteredItems.map(item => (
<li key={item.id}>{item.name}</li>
))}
</ul>
</div>
);
}

export default App;
SearchFilter.module.css
.search-filter-container {
display: flex;
flex-direction: column;
margin-bottom: 1rem;
}

.search-filter-label {
font-size: 0.875rem;
font-weight: bold;
margin-bottom: 0.25rem;
}

.search-filter-input {
border: 1px solid #ccc;
border-radius: 0.25rem;
padding: 0.5rem;
font-size: 0.875rem;
line-height: 1.25rem;
}