Skip to content
Advertisement

TextInput gets unfocused after typing each character

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;

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);
  }
};
Advertisement