DebounceControl Component

What is debounce? Debounce is a way to delay a function until an action stops. For example, in a search bar, instead of running a search every time you press a key, debouncing waits until you finish typing, then runs the search just once. This keeps the app fast by avoiding too many repeated actions.

But we already have a way to debounce state either using hook libraries like useDebounce or creating your own. So, why do we need this?

Let’s step back and see how useDebounce work:

import React, { useState } from "react";
import { useDebounce } from "some-where";

const App = () => {
  const [text, setText] = useState("");

  const debouncedText = useDebounce(text, 1000); // 1000ms

  // do something with this `debouncedText`

  return (
     setText(e.target.value)}
    />
  );
};

export default App;

With each keystroke, we update the state with setText, and when the user stops typing for 1000ms, debouncedText finally updates. Ideally, this is when we’d make an API call.

Now, here’s the problem: even though debouncedText only updates once, the text state still updates on each keystroke, causing the entire app to re-render on every input. This might not be an issue in this example, where we're just rendering a simple input element.

Lets take a real world example:

An example why we need DebounceControl

The code might look something like this:

import React, { useState } from "react";
import { useQuery } from 'react-query';
import { useDebounce } from "some-where";

const UserListPage = () => {
  const [state, setState] = useState(initialState);

  const debouncedState = useDebounce(state, 1000); // 1000ms

  const { data } = useQuery({
    queryKey: ['user', debouncedState],
    queryFn: () => fetchUserList(debouncedState),
  });

  return (
    
      
      
    
  );
};

export default UserListPage;

Now, do you see the problem? On each keystroke in any of the input fields, everything has to re-render, including the UserList component, even though nothing related to it has changed.

Now, this is where comes into play.

import React, { useState } from "react";
import DebounceControl from "debounce-control";

const App = () => {
  const [text, setText] = useState("");

  const onDebouncedChange = (value) => {
    setText(value);
  }

  return (
     (
         onChange(e.target.value)}
        />
      )}
    />
  );
};

export default App;

Notice how DebounceControl decouples the debouncing logic from the rest of the app. With this, the text state only updates after debouncing occurs, and in the previous example, the UserList or the entire component will no longer re-render on each keystroke.

Big question now. Why should we use this?

  1. Minimal re-renders
  2. Works with any form element (not limited to input or textarea). Also, the element is right there if you want to add a ref or modify props.
  3. Type-safe

Demo:

Input component

High re-renders before use of DebounceControl
Minimal re-renders after use of DebounceControl

Slider component

Use of DebounceControl with Slider Component

Resizable component

Use of DebounceControl with Resizable Component

NPM: https://www.npmjs.com/package/debounce-control

Conclusion:
By using , you can easily debounce any form element while keeping your app performant and type-safe.