2

Basics

I created a simple component with a search field and some state to update the users input. In order to (theoretically) improve performance, I wrote a debounce function to prevent the handleChange from firing on every keystroke.

Problem

e.target.value is always an empty string when the function fires. I tried to use e.persist() to make the component remember the value, but it gets lost somewhere. So the <input /> appears always empty.

Code

const ShopSearch = () => {
  const [search, setSearch] = React.useState("");

  const handleChange = e => {
    e.persist();
    const value = e.target.value;
    setSearch(value);
  };

  function debounce(func) {
    let timer;

    return (...args) => {
      clearTimeout(timer);

      timer = setTimeout(() => func.apply(this, args), 300);
    };
  }

  return (
    <div>
      <form role="search">
        <input
          placeholder="Search for a shop"
          value={search}
          onChange={debounce(handleChange)}
        />
      </form>

      <Query url="https://shop-search-api.blabla.com/shopsearch?searchTerm=hemden&page=0">
        {({ shops }) =>
          shops
            .filter(shop =>
              search.length > 1
                ? shop.shopName.toLowerCase().includes(search.toLowerCase())
                : false
            )
            .map(shop => <ShopOverview key={shop.tsID} {...shop} />)
        }
      </Query>
    </div>
  );
};

What am I doing wrong? I guess the synthetic event gets lost somewhere because it is not there anymore when the function gets finally executed? Can someone explain it to me or point me to some resources?

Gh05d
  • 7,923
  • 7
  • 33
  • 64
  • 1
    You are calling `e.persist()` too late. You basically have to call it "synchronously" when the event happens, e.g. `const handler = debounce(handleChange); .... onChange={e => {e.persist(); handleChange(e);}}`. Maybe this helps: [Perform debounce in React.js](https://stackoverflow.com/questions/23123138/perform-debounce-in-react-js) – Felix Kling Jul 26 '21 at 14:16

1 Answers1

0

You are using debounce function inside the component which makes it re-eveluate everytime when the component renders.

Try keeping it either outside of the Component or in the helper functions.

And also use useCallback hook to have a function which doesn't change on App rerenders


function debounce(func) {
  let timer;

  return (...args) => {
    clearTimeout(timer);

    timer = setTimeout(() => func.apply(this, args), 300);
  };
}

const ShopSearch = () => {
  const [search, setSearch] = React.useState("");

  const handleChange = e => {
    e.persist();
    const value = e.target.value;
    setSearch(value);
  };

  const debouncedHandleChange = React.useCallback(
    debounce(handleChange, 500),
    []
  );

  return (
    <div>
      <form role="search">
        <input
          placeholder="Search for a shop"
          value={search}
          onChange={debouncedHandleChange}
        />
      </form>

      <Query url="https://shop-search-api.blabla.com/shopsearch?searchTerm=hemden&page=0">
        {({ shops }) =>
          shops
            .filter(shop =>
              search.length > 1
                ? shop.shopName.toLowerCase().includes(search.toLowerCase())
                : false
            )
            .map(shop => <ShopOverview key={shop.tsID} {...shop} />)
        }
      </Query>
    </div>
  );
};

Mahesh S
  • 11
  • 2