Skip to main content
Manages local state updates with debounce timing to reduce rapid re-renders. Optionally applies the first update immediately with leading.

Signature

function useDebouncedState<T>(
  defaultValue: T,
  wait: number,
  options?: UseDebouncedStateOptions
): UseDebouncedStateReturnValue<T>

Parameters

defaultValue
T
required
Initial state value.
wait
number
required
Debounce delay in milliseconds.
options
UseDebouncedStateOptions
Optional debounce behavior configuration.

Returns

result
[T, (newValue: SetStateAction<T>) => void]
A tuple containing the current value and a setter function.

Usage

Basic debounced state

import { useDebouncedState } from '@kuzenbo/hooks';

function SearchInput() {
  const [value, setValue] = useDebouncedState('', 500);

  return (
    <div>
      <input
        onChange={(e) => setValue(e.target.value)}
        placeholder="Type to search..."
      />
      <p>Debounced value: {value}</p>
    </div>
  );
}

With leading edge

import { useDebouncedState } from '@kuzenbo/hooks';
import { useEffect } from 'react';

function Counter() {
  const [count, setCount] = useDebouncedState(0, 1000, { leading: true });

  useEffect(() => {
    console.log('Count updated:', count);
  }, [count]);

  return (
    <div>
      <button onClick={() => setCount(count + 1)}>Increment</button>
      <p>Count: {count}</p>
    </div>
  );
}

Functional updates

import { useDebouncedState } from '@kuzenbo/hooks';

function TodoList() {
  const [items, setItems] = useDebouncedState<string[]>([], 300);

  const addItem = (item: string) => {
    setItems((prev) => [...prev, item]);
  };

  return (
    <div>
      <button onClick={() => addItem('New item')}>Add Item</button>
      <ul>
        {items.map((item, i) => (
          <li key={i}>{item}</li>
        ))}
      </ul>
    </div>
  );
}

Form input with debounced validation

import { useDebouncedState } from '@kuzenbo/hooks';
import { useEffect, useState } from 'react';

function EmailInput() {
  const [email, setEmail] = useDebouncedState('', 500);
  const [isValid, setIsValid] = useState(true);

  useEffect(() => {
    if (email) {
      setIsValid(email.includes('@'));
    }
  }, [email]);

  return (
    <div>
      <input
        onChange={(e) => setEmail(e.target.value)}
        placeholder="Enter email"
      />
      {!isValid && <p>Invalid email address</p>}
    </div>
  );
}

Build docs developers (and LLMs) love