I’m using React to build a form and I’m trying to filter a list
with the SearchInput
(which works the same as TextInput) located in the child component Header
. But everytime I type a character the SearchInput gets unfocused
function index() { const list = [//data\] const [search, setSearch] = useState(""); const [filteredResults, setFilteredResults] = useState([]); const searchItems = (searchValue) => { setSearch(searchValue); if (search !== "") { const filteredData = partners.filter((item) => { return Object.values(item) .join("") .toLowerCase() .includes(search.toLowerCase()); }); setFilteredResults(filteredData); } else { setFilteredResults(partners); } }; const Header = () => ( <Box> <SearchInput placeholder="Search" value={search} onChange={(e) => searchItems(e.target.value)} /> </Box> ); return ( <Parent headerContent={<Header />} > <Box> <Table data={search.length > 1 ? filteredResults : list} /> </Box> </Parent> ); } export default index;
Advertisement
Answer
Oh, I think I can see the problem now – it’s the way you’re rendering the <SearchInput />
component. You’re inadvertantly creating a new functional component on every render. Either inline the Header
directly into the Parent
control’s headerContent
property, or create an entirely separate component:
const Header = ({ search, onSearchChange }) => { const handleChange = (e) => onSearchChange(e.target.value); return ( <Box> <SearchInput placeholder="Search" value={search} onChange={handleChange} /> </Box> ); } function index() { // ----- 8< ----- return ( <Parent headerContent={<Header search={search} onSearchChange={searchItems} />} > {/* ... */} </Parent> ); }
While you’re there, you have a subtle bug with your comparison – it looks like you’re searching your partners
effectively as a list of strings; but, since you’re joining them, if you had partners with the names:
'one' 'two'
You’re creating a search string as ‘onetwo’ – so searching for ‘et’ would match, even though you don’t actually have a partner matching that. You can fix that by just checking each partner individually… something like:
const searchItems = (searchValue) => { setSearch(searchValue); if (search !== "") { const searchValueLower = searchValue.toLowerCase(); const filteredData = partners.filter((item) => { return Object.values(item) .some(item => item.toLowerCase().includes(searchValueLower); }); setFilteredResults(filteredData); } else { setFilteredResults(partners); } };