# Debounce and persist event targeting in react **Published by:** [Server Surfer](https://paragraph.com/@serversurfer/) **Published on:** 2021-11-18 **URL:** https://paragraph.com/@serversurfer/debounce-and-persist-event-targeting-in-react ## Content Recently I worked on a search component that would communicate with an API that uses ElasticSearch for indexing. It's common to have a search input, that as you type will auto query and update the list on the frontend. So I used a text input, and inside the `onChange` event would pass the `event.target.value` as a parameter to the API url. It would look something like this (abbreviated for readability): const searchQueryFunction = (queryText) => { const res = await fetch(`/products.json?_page=${page}&_limit=10&_q=${queryText}&_total=50`, { method: 'GET', headers: { 'Access-Control-Allow-Origin': '*' } }) const responseData = await res.json() setItems({items: responseData.data}) } <input type="text" placeholder="Search..." onChange=((e) => searchQueryFunction(e.target.value)) /> But I quickly found out how taxing this was for the browser and server. As I would search for something like "Minivan", it would query each letter. This is where a debouncer comes in. There are libraries like `lodash` that offer a function, but wanted to keep my `package.json` light. A debouncer is just a fancy `setTimeout`, so I created a `Debouncer` class that takes 2 parameters. A function and time. It looks like this: export default class Debouncer { timeout: null | ReturnType<typeof setTimeout> n: number func: Function constructor(func, n) { this.timeout = null this.n = n || 500 this.func = func } execute = (e) => { this.cancel() this.timeout = setTimeout(() => { this.func(e) }, this.n) } cancel = () => { if (this.timeout) clearTimeout(this.timeout) } } Inside the React component, initialize the Debouncer and pass it the searchQueryFunction const debouncer = new Debouncer((e) => searchQueryFunction(e)) Then create a new function that will execute the searchQueryFunction through the debouncer const execDebouncer = (e) => debouncer.execute(e) Now inside the input onChange event, well replace calling the searchQueryFunction directly and use the new execDebouncer <input type="text" onChange={(e) => execDebouncer(e)} /> But.. you'll notice some errors in the log: "This synthetic event is reused for performance reasons. If you're seeing this, you're accessing the method currentTarget on a released/nullified synthetic event. This is a no-op function. If you must keep the original synthetic event around, use event.persist()" This is because the event is lost, while the function is being debounced for 500ms. To fix this, simply add e.persist() inside the execDebouncer function const execDebouncer = (e) => { e.persist() return debouncer.execute(e) } I put together a little example, with an included mock api: https://codesandbox.io/embed/pensive-swartz-cvsx1 ## Publication Information - [Server Surfer](https://paragraph.com/@serversurfer/): Publication homepage - [All Posts](https://paragraph.com/@serversurfer/): More posts from this publication - [RSS Feed](https://api.paragraph.com/blogs/rss/@serversurfer): Subscribe to updates - [Twitter](https://twitter.com/dillonraphael): Follow on Twitter