DEV Community

Cover image for Debounce Control: React component to debounce state
Pranay Bajracharya
Pranay Bajracharya

Posted on

1

Debounce Control: React component to debounce state

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 (
    <input
      type="text"
      value={text}
      onChange={(e) => setText(e.target.value)}
    />
  );
};

export default App;
Enter fullscreen mode Exit fullscreen mode

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 (
    <div>
      <UserFilter state={state} setState={setState} />
      <UserList data={data} />
    </div>
  );
};

export default UserListPage;
Enter fullscreen mode Exit fullscreen mode

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 <DebounceControl /> 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 (
    <DebounceControl
      value={text}
      delay={1000}
      onDebouncedChange={onDebouncedChange}
      render={({ value, onChange }) => (
        <input
          type="text"
          value={value}
          onChange={(e) => onChange(e.target.value)}
        />
      )}
    />
  );
};

export default App;
Enter fullscreen mode Exit fullscreen mode

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 <DebounceControl />, you can easily debounce any form element while keeping your app performant and type-safe.

Sentry image

See why 4M developers consider Sentry, “not bad.”

Fixing code doesn’t have to be the worst part of your day. Learn how Sentry can help.

Learn more

Top comments (0)

Hot sauce if you're wrong - web dev trivia for staff engineers

Hot sauce if you're wrong · web dev trivia for staff engineers (Chris vs Jeremy, Leet Heat S1.E4)

  • Shipping Fast: Test your knowledge of deployment strategies and techniques
  • Authentication: Prove you know your OAuth from your JWT
  • CSS: Demonstrate your styling expertise under pressure
  • Acronyms: Decode the alphabet soup of web development
  • Accessibility: Show your commitment to building for everyone

Contestants must answer rapid-fire questions across the full stack of modern web development. Get it right, earn points. Get it wrong? The spice level goes up!

Watch Video 🌶️🔥

👋 Kindness is contagious

DEV shines when you're signed in, unlocking a customized experience with features like dark mode!

Okay