475

useDebounceState

Hook that creates a debounced state

utilitieshightest coverage

Welcome to reactuse

A collection of React hooks for everyday tasks.

Quick start

Install via npm and you get zero dependencies, TypeScript types out of the box, and fully tested hooks ready to use.

Check it out at reactuse.com.

import { useDebounceState } from '@siberiacancode/reactuse';
import { useState } from 'react';

import { cn } from '@/utils/lib';

const DEFAULT_MARKDOWN = `# Welcome to reactuse\n\nA collection of **React hooks** for everyday tasks.\n\n## Quick start\n\nInstall via npm and you get **zero dependencies**, **TypeScript types** out of the box, and **fully tested** hooks ready to use.\n\nCheck it out at [reactuse.com](https://reactuse.com).`;

const formatInline = (text: string) =>
  text
    .replace(/\*\*(.+?)\*\*/g, '<b>$1</b>')
    .replace(/\*(.+?)\*/g, '<i>$1</i>')
    .replace(/`(.+?)`/g, '<code>$1</code>')
    .replace(/\[(.+?)\]\((.+?)\)/g, '<a href="$2" target="_blank" rel="noreferrer">$1</a>');

const renderMarkdown = (markdown: string) => {
  const lines = markdown.split('\n');
  const html: string[] = [];

  for (const line of lines) {
    const trimmed = line.trim();
    if (!trimmed) continue;

    if (trimmed.startsWith('# ')) {
      html.push(`<h2>${trimmed.slice(2)}</h2>`);
      continue;
    }

    if (trimmed.startsWith('## ')) {
      html.push(`<h3>${trimmed.slice(3)}</h3>`);
      continue;
    }

    html.push(`<p>${formatInline(trimmed)}</p>`);
  }

  return html.join('');
};

const Demo = () => {
  const [markdown, setMarkdown] = useDebounceState(DEFAULT_MARKDOWN, 400);
  const [tab, setTab] = useState<'edit' | 'preview'>('edit');

  return (
    <section className='flex w-full max-w-3xl flex-col gap-4 p-4'>
      <div className='block md:hidden!' data-slot='tabs'>
        <div data-slot='tabs-list'>
          <button
            data-state={tab === 'edit' ? 'active' : 'inactive'}
            data-variant='tabs-trigger'
            type='button'
            onClick={() => setTab('edit')}
          >
            Edit
          </button>
          <button
            data-state={tab === 'preview' ? 'active' : 'inactive'}
            data-variant='tabs-trigger'
            type='button'
            onClick={() => setTab('preview')}
          >
            Preview
          </button>
        </div>
      </div>

      <div className='grid grid-cols-1 gap-3 md:grid-cols-2'>
        <textarea
          className={cn(
            'no-scrollbar h-72! resize-none font-mono! text-xs!',
            tab === 'edit' ? 'block!' : 'hidden!',
            'md:block!'
          )}
          defaultValue={DEFAULT_MARKDOWN}
          onChange={(event) => setMarkdown(event.target.value)}
        />

        <div
          className={cn(
            'bg-muted/40 prose prose-xs dark:prose-invert no-scrollbar h-72 max-w-none overflow-y-auto rounded-lg p-4 text-xs',
            tab === 'preview' ? 'block!' : 'hidden!',
            'md:block!'
          )}
          dangerouslySetInnerHTML={{ __html: renderMarkdown(markdown) }}
        />
      </div>
    </section>
  );
};

export default Demo;

Installation

pnpm add @siberiacancode/reactuse

Usage

const [debouncedValue, setDebouncedValue] = useDebounceState(value, 500);

API

Parameters

NameTypeDefaultNote
valueValue-The value to be debounced
delaynumber-The delay in milliseconds

Returns

[Value, (value: Value) => void]

Contributors

ddebabin

Last updated on