SPB Git forge
38commits 1branches 0releases
338.7 MBsize
maindefault branch
4 h agolast push
HTML 53.9% TypeScript 44.5% JavaScript 0.6% SQL 0.5%
2.4 KB · 89 lines typescript
Raw Blame History
1"use client";23import { useCallback, useEffect, useMemo, useRef, useState, useSyncExternalStore } from "react";45export function useDebounced<T>(value: T, ms = 200): T {6  const [v, setV] = useState(value);7  useEffect(() => {8    const t = setTimeout(() => setV(value), ms);9    return () => clearTimeout(t);10  }, [value, ms]);11  return v;12}1314export function useMediaQuery(q: string, initial = false): boolean {15  const [m, setM] = useState(initial);16  useEffect(() => {17    const mq = window.matchMedia(q);18    const on = () => setM(mq.matches);19    on();20    mq.addEventListener("change", on);21    return () => mq.removeEventListener("change", on);22  }, [q]);23  return m;24}2526const LS_EVENT = "dci:localstorage";27function readRaw(key: string): string | null {28  try {29    return window.localStorage.getItem(key);30  } catch {31    return null;32  }33}3435/** localStorage-backed state (JSON), SSR-safe (server snapshot = initial), synced across tabs. */36export function useLocalStorage<T>(key: string, initial: T): [T, (v: T | ((p: T) => T)) => void] {37  const subscribe = useCallback((cb: () => void) => {38    window.addEventListener("storage", cb);39    window.addEventListener(LS_EVENT, cb);40    return () => {41      window.removeEventListener("storage", cb);42      window.removeEventListener(LS_EVENT, cb);43    };44  }, []);45  const raw = useSyncExternalStore(subscribe, () => readRaw(key), () => null);46  const value = useMemo<T>(() => {47    if (raw === null) return initial;48    try {49      return JSON.parse(raw) as T;50    } catch {51      return initial;52    }53  }, [raw, initial]);54  const set = useCallback(55    (nv: T | ((p: T) => T)) => {56      const cur = ((): T => {57        const r = readRaw(key);58        if (r === null) return initial;59        try {60          return JSON.parse(r) as T;61        } catch {62          return initial;63        }64      })();65      const next = typeof nv === "function" ? (nv as (p: T) => T)(cur) : nv;66      try {67        window.localStorage.setItem(key, JSON.stringify(next));68      } catch {69        /* ignore */70      }71      window.dispatchEvent(new Event(LS_EVENT));72    },73    [key, initial],74  );75  return [value, set];76}7778export function useInterval(fn: () => void, ms: number | null) {79  const ref = useRef(fn);80  useEffect(() => {81    ref.current = fn;82  });83  useEffect(() => {84    if (ms === null) return;85    const t = setInterval(() => ref.current(), ms);86    return () => clearInterval(t);87  }, [ms]);88}89