From c1f03f7f9548fd4166aee789bfde0543a29753af Mon Sep 17 00:00:00 2001
From: saifmohamedsv
-
+
@@ -119,6 +119,14 @@ function Component() {
- **[`useScript`](https://hookli.vercel.app/docs/use-script)** — Load an external script and report its load status.
- **[`useFetch`](https://hookli.vercel.app/docs/use-fetch)** — Declarative fetch with loading and error status.
- **[`useGeoLocation`](https://hookli.vercel.app/docs/use-geo-location)** — Browser geolocation state.
+- **[`usePrevious`](https://hookli.vercel.app/docs/use-previous)** — Track a value from the previous render.
+- **[`useList`](https://hookli.vercel.app/docs/use-list)** — Array state with push, insert, update, remove, and clear helpers.
+- **[`useSet`](https://hookli.vercel.app/docs/use-set)** — Set state with add, remove, toggle, has, and clear helpers.
+- **[`useThrottle`](https://hookli.vercel.app/docs/use-throttle)** — Throttle a fast-changing value to at most one update per interval.
+- **[`useUpdateEffect`](https://hookli.vercel.app/docs/use-update-effect)** — A useEffect that skips the initial mount and runs only on updates.
+- **[`useEffectOnce`](https://hookli.vercel.app/docs/use-effect-once)** — Run an effect exactly once, on mount.
+- **[`useKeyPress`](https://hookli.vercel.app/docs/use-key-press)** — Track whether a specific key is currently held down.
+- **[`useWindowScroll`](https://hookli.vercel.app/docs/use-window-scroll)** — Track the window scroll position reactively.
## 🧪 TypeScript
diff --git a/packages/hookli/README.md b/packages/hookli/README.md
index ea52f77..03c07c7 100644
--- a/packages/hookli/README.md
+++ b/packages/hookli/README.md
@@ -27,7 +27,7 @@
-
+
@@ -117,6 +117,14 @@ function Component() {
- **[`useScript`](https://hookli.vercel.app/docs/use-script)** — Load an external script and report its load status.
- **[`useFetch`](https://hookli.vercel.app/docs/use-fetch)** — Declarative fetch with loading and error status.
- **[`useGeoLocation`](https://hookli.vercel.app/docs/use-geo-location)** — Browser geolocation state.
+- **[`usePrevious`](https://hookli.vercel.app/docs/use-previous)** — Track a value from the previous render.
+- **[`useList`](https://hookli.vercel.app/docs/use-list)** — Array state with push, insert, update, remove, and clear helpers.
+- **[`useSet`](https://hookli.vercel.app/docs/use-set)** — Set state with add, remove, toggle, has, and clear helpers.
+- **[`useThrottle`](https://hookli.vercel.app/docs/use-throttle)** — Throttle a fast-changing value to at most one update per interval.
+- **[`useUpdateEffect`](https://hookli.vercel.app/docs/use-update-effect)** — A useEffect that skips the initial mount and runs only on updates.
+- **[`useEffectOnce`](https://hookli.vercel.app/docs/use-effect-once)** — Run an effect exactly once, on mount.
+- **[`useKeyPress`](https://hookli.vercel.app/docs/use-key-press)** — Track whether a specific key is currently held down.
+- **[`useWindowScroll`](https://hookli.vercel.app/docs/use-window-scroll)** — Track the window scroll position reactively.
## 🧪 TypeScript
diff --git a/packages/hookli/ROADMAP.md b/packages/hookli/ROADMAP.md
new file mode 100644
index 0000000..f299b87
--- /dev/null
+++ b/packages/hookli/ROADMAP.md
@@ -0,0 +1,53 @@
+# hookli — Roadmap
+
+Tracking doc for growing the hook catalog and the package DX. Work proceeds in **tiers**;
+check items off as they land. Each hook has three tracks:
+
+- **lib** — hook + colocated vitest test + barrel + manifest entry (ships to npm)
+- **docs** — rich doc-page entry in `apps/docs/lib/hook-docs.ts` (usage, API tables, live demo)
+- *(the docs sidebar/index/count auto-derive from the manifest — a hook appears the moment `lib` lands; `docs` just adds the demo + usage)*
+
+> Count today: **41 → target ~55** after Tiers 1–2.
+
+---
+
+## Tier 1 — expected-pair gaps (the "why isn't this here?" hooks)
+
+| hook | category | lib | docs | notes |
+|---|---|:--:|:--:|---|
+| `useThrottle` | effects | ✅ | ⬜ | the twin of `useDebounce` |
+| `usePrevious` | state | ✅ | ⬜ | previous render's value |
+| `useUpdateEffect` | effects | ✅ | ⬜ | effect that skips first render |
+| `useEffectOnce` | effects | ✅ | ⬜ | run-once effect |
+| `useList` | state | ✅ | ⬜ | array state (push/remove/clear) — sibling of `useMap` |
+| `useSet` | state | ✅ | ⬜ | Set state — completes map/set/list trio |
+| `useKeyPress` | dom | ✅ | ⬜ | true while a key is held |
+| `useWindowScroll` | dom | ✅ | ⬜ | reactive `{ x, y }` scroll position |
+
+> **Tier 1 lib: ✅ shipped** (41 → 49 hooks, +20 tests, gate green). Docs demos pending.
+
+## Tier 2 — fill the thin `data` category (only 2 today)
+
+| hook | category | lib | docs | notes |
+|---|---|:--:|:--:|---|
+| `useAsync` | data | ⬜ | ⬜ | run an async fn → `{ loading, error, value }` |
+| `useMutation` | data | ⬜ | ⬜ | async write action with status |
+| `usePagination` | data | ⬜ | ⬜ | page/limit/offset state helper |
+| `useNetworkState` | data | ⬜ | ⬜ | online/offline + connection info |
+| `usePageVisibility` | effects | ⬜ | ⬜ | tab focus/blur via `visibilitychange` |
+| `useIdle` | dom | ⬜ | ⬜ | user-inactivity detection |
+
+## Tier 3 — rounding-out (backlog)
+`useQueue` · `useDefault` · `useLongPress` · `useFullscreen` · `useTextSelection` ·
+`useRafState` · `useDeepCompareEffect` · `useBattery` · `usePermission` · `useHotkeys`
+
+---
+
+## Package & DX enhancements (not hooks)
+
+- ⬜ `size-limit` in CI + a per-hook gzipped-size badge ("every hook < 0.5 kB")
+- ⬜ Hook generator script (`pnpm new:hook use-foo`) — scaffolds folder + test + manifest entry
+- ⬜ Per-hook subpath exports (`import { useToggle } from "hookli/use-toggle"`)
+- ⬜ Changesets — automated versioning + changelog (also fixes git-tag ↔ npm drift)
+- ⬜ "Compare vs react-use / usehooks-ts" table in the docs
+- ⬜ StackBlitz "open in playground" per hook page
diff --git a/packages/hookli/hooks.manifest.json b/packages/hookli/hooks.manifest.json
index 468ba53..a963e23 100644
--- a/packages/hookli/hooks.manifest.json
+++ b/packages/hookli/hooks.manifest.json
@@ -287,6 +287,62 @@
"description": "Browser geolocation state.",
"category": "data",
"signature": "useGeoLocation(): { location: GeolocationPosition | null; error: GeolocationError | Error | null }"
+ },
+ {
+ "slug": "use-previous",
+ "name": "usePrevious",
+ "description": "Track a value from the previous render.",
+ "category": "state",
+ "signature": "usePrevious jgX
zF^*o=Y#eb0+7(Bx=)#QCRGg;L(3%}0R}NY1Hneu7Z*k7|I7mq;BP+XIPI7%8Z0;Cc
z3EuYx!Frp6gLc`#UG=)Bb!zZEkPD-?frx{zGry&`81FJyBze=ywR6gIR=zPQ&Oas{
z$S?Oe1}3ODb=iCXA{3{W!LeU1BhfqJDR1I_cob82*QIC4HeJIikAJHjv;5V;K^&y-
zNqq@D5p|QkgYOK`L43@C=nCwu@QzO8f0aG{1Ru5r_P<6%BLAKFK`=}C+x);g%T|4+
z)wq@=IIJ5e&8@#gs&rj$BwEVM_KW6ngQ@VRJlVRd^t_?E8qUooez`a;OYr2!=PiJX
z^BaLbY%=v^R^ON0X`ujnWq4f``qTI{N5!vfylb8
pWAt4O42#{do}twB*S+gb)fEn!^M4ihOaSl&6!kQKC=4qlpBCDVnbF<*
zCS2ULbh1k1*br%3KaK*K{pK~BGG1v>(%lc#*vpHev5^Zc%)t<|2L%vK1^vjY)7oL!
z-XXM=M$fe;667@gBV}H*EqFe@!D2?GAv;8qx6KW)sxY$`KXPJg?VG{I