Utility Hooks

Common utility Hooks

useLocalStorage

React wrapper for localStorage.

import { useLocalStorage } from '@linch-tech/desktop-core';

function Counter() {
  const [count, setCount] = useLocalStorage('counter', 0);

  return (
    <button onClick={() => setCount(count + 1)}>
      Click count: {count}
    </button>
  );
}

Signature

function useLocalStorage<T>(
  key: string,
  initialValue: T
): [T, (value: T | ((val: T) => T)) => void]

useAsync

Async operation state management.

import { useAsync } from '@linch-tech/desktop-core';

function UserProfile({ userId }: { userId: string }) {
  const { data, loading, error, execute } = useAsync(
    () => fetchUser(userId),
    [userId]
  );

  if (loading) return <div>Loading...</div>;
  if (error) return <div>Error: {error.message}</div>;
  if (!data) return null;

  return <div>{data.name}</div>;
}

Return Value

interface UseAsyncReturn<T> {
  data: T | null;
  loading: boolean;
  error: Error | null;
  execute: () => Promise<T>;
}

useFetch

HTTP request wrapper.

import { useFetch } from '@linch-tech/desktop-core';

function UserList() {
  const { data, loading, error } = useFetch<User[]>('/api/users');

  if (loading) return <div>Loading...</div>;
  if (error) return <div>Error: {error.message}</div>;

  return (
    <ul>
      {data?.map(user => <li key={user.id}>{user.name}</li>)}
    </ul>
  );
}

useDebounce

Value debouncing.

import { useDebounce } from '@linch-tech/desktop-core';

function SearchInput() {
  const [value, setValue] = useState('');
  const debouncedValue = useDebounce(value, 300);

  useEffect(() => {
    // Only triggers when user stops typing for 300ms
    searchApi(debouncedValue);
  }, [debouncedValue]);

  return (
    <input
      value={value}
      onChange={(e) => setValue(e.target.value)}
      placeholder="Search..."
    />
  );
}

useDebouncedCallback

Callback debouncing.

import { useDebouncedCallback } from '@linch-tech/desktop-core';

function Editor() {
  const saveToServer = useDebouncedCallback(
    (content: string) => {
      api.save(content);
    },
    500
  );

  return (
    <textarea onChange={(e) => saveToServer(e.target.value)} />
  );
}

useThrottle

Value throttling.

import { useThrottle } from '@linch-tech/desktop-core';

function ScrollPosition() {
  const [scrollY, setScrollY] = useState(0);
  const throttledScrollY = useThrottle(scrollY, 100);

  useEffect(() => {
    const handleScroll = () => setScrollY(window.scrollY);
    window.addEventListener('scroll', handleScroll);
    return () => window.removeEventListener('scroll', handleScroll);
  }, []);

  return <div>Scroll position: {throttledScrollY}</div>;
}

useThrottledCallback

Callback throttling.

import { useThrottledCallback } from '@linch-tech/desktop-core';

function MouseTracker() {
  const handleMouseMove = useThrottledCallback(
    (e: MouseEvent) => {
      console.log(e.clientX, e.clientY);
    },
    100
  );

  useEffect(() => {
    window.addEventListener('mousemove', handleMouseMove);
    return () => window.removeEventListener('mousemove', handleMouseMove);
  }, [handleMouseMove]);

  return null;
}

useClickOutside

Click outside detection.

import { useRef } from 'react';
import { useClickOutside } from '@linch-tech/desktop-core';

function Dropdown() {
  const [isOpen, setIsOpen] = useState(false);
  const ref = useRef<HTMLDivElement>(null);

  useClickOutside(ref, () => {
    setIsOpen(false);
  });

  return (
    <div ref={ref}>
      <button onClick={() => setIsOpen(true)}>Open</button>
      {isOpen && <div className="dropdown-menu">Menu content</div>}
    </div>
  );
}

useClickOutsideMultiple

Multiple elements click outside detection.

import { useClickOutsideMultiple } from '@linch-tech/desktop-core';

function ComplexDropdown() {
  const triggerRef = useRef<HTMLButtonElement>(null);
  const menuRef = useRef<HTMLDivElement>(null);

  useClickOutsideMultiple([triggerRef, menuRef], () => {
    setIsOpen(false);
  });

  return (
    <>
      <button ref={triggerRef}>Trigger</button>
      <div ref={menuRef}>Menu</div>
    </>
  );
}

useEscapeKey

ESC key detection.

import { useEscapeKey } from '@linch-tech/desktop-core';

function Modal({ onClose }: { onClose: () => void }) {
  useEscapeKey(onClose);

  return (
    <div className="modal">
      <p>Press ESC to close</p>
    </div>
  );
}

useClickOutsideOrEscape

Combined: click outside or press ESC to close.

import { useClickOutsideOrEscape } from '@linch-tech/desktop-core';

function Popover() {
  const [isOpen, setIsOpen] = useState(false);
  const ref = useRef<HTMLDivElement>(null);

  useClickOutsideOrEscape(ref, () => setIsOpen(false));

  return (
    <div ref={ref}>
      {isOpen && <div>Click outside or press ESC to close</div>}
    </div>
  );
}