Home > other >  Debounce function not working properly when input is changed at fast pace
Debounce function not working properly when input is changed at fast pace

Time:07-06

function Test() {

  // const [value, setValue] = useState('')
  // const [display, setDisplay] = useState(false)

  let value = '';
  let display = false;

  /* Debounce function */
  const handleChange = (e) => {
    // setValue(e.target.value)

    let timer;

    if (timer) {
      clearTimeout(timer)
    }

    if (value.length === 1 && e.target.value.length === 0) {
      // setDisplay(false)
    }
    else {
      timer = setTimeout(() => {
        // setDisplay(true)
        // dispatch(loadSearchResult(e.target.value, fiatCurrencyUuid))

        console.log(e.target && e.target.value);

      }, 500)
    }
  }

  return (
    <div>
      <input type='text' placeholder='Search' value={value} onChange={e => handleChange(e)} />

      {
        display
        ? result
          .map(item => {
            return (
              <li key={item.id}>{item.id}</li>
            );
          })
        : undefined
      }
    </div>
  );
}

// export default Test
ReactDOM.render(<Test/>, document.querySelector("#test"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>

<div id="test"></div>

I have a debounce function which executes 500ms after the user stopped typing in the input. The debounce function set the display to true when something is typed and to false if there is only one letter present in the input so as to close the div modal. It is working fine if the input is changing one letter by letter but if it has for example "hello" text and I clear the input very fastly without letting the timeout to run again, then the display is first set to false and the div modal closes but suddenly it automatically becomes true and the div modal again renders.

CodePudding user response:

The issue has to do on how react works (which is complicated), every time you call the setValue function and runs the rest of the debounce logic, this dispatches a command to update the value sometime in the future (very small time, almost instantly, but it is still asynchronous and not in the current execution loop). When the value gets updated, react will call the component function again to show the updated value on the screen, also creating a new handleChange function, with a new timer variable that has nothing todo with the previous one, for this reason, every change you make on the input, creates a new timer without cancelling the previous, and eventually all of them will execute in sequence.

To fix this you can hold the timer instance in a react ref, this way, all the handleChange functions created over time will still use the same variable reference and it will work properly:

    const [value, setValue] = useState('')

    const [display, setDisplay] = useState(false)

    const timer = useRef()

    /* Debounce function */
    const handleChange = (e) => {
        setValue(e.target.value)

        if (timer.current) {
            clearTimeout(timer.current)
        }
        
        if (value.length === 1 && e.target.value.length === 0) {
            setDisplay(false)
        }
        else {
            timer.current = setTimeout(() => {
                setDisplay(true)
                dispatch(loadSearchResult(e.target.value, fiatCurrencyUuid))
            }, 500)
        }
    }

    return (
        <>
          <input type='text' placeholder='Search' value={value} onChange={e => handleChange(e)} />

          {display ? 
             result.map(item => {
                 return <li key={item.id} /> : undefined
          }
        </>
    )
  • Related