Skip to content

Releases: childrentime/reactuse

v6.5.0

Choose a tag to compare

@childrentime childrentime released this 05 Aug 11:59

📦 Build

Per-module dist: optimizePackageImports now works — #216

The published dist previously inlined all 120+ hooks into a single bundle, so barrel-file optimizers had nothing to unroll: a Next.js (Turbopack) dev page importing just useDebounce pulled in every hook — a 552 kB client chunk. The build now ships one file per module (tsdown with unbundle, i.e. preserveModules) with the entry as a thin barrel of re-exports, and the same page loads only useDebounce and its real dependency chain: 64 kB (−88%).

With this release, Next.js users can enable:

// next.config.js
module.exports = {
  experimental: {
    optimizePackageImports: ['@reactuses/core']
  }
}

✨ Direct subpath imports

Every hook is now importable directly, skipping the barrel entirely — no bundler configuration needed:

import { useDebounce } from '@reactuses/core/useDebounce'

Notes

  • require entries now resolve to ./dist/index.js / ./dist/index.d.ts (previously .cjs / .d.cts); ./useQRCode moved to ./dist/useQRCode/index.*. Both were only reachable through the exports map, so consumers are unaffected.
  • Build tool switched from bunchee to tsdown; bunchee dropped from the root workspace.

Full PR: #216

v6.4.2

Choose a tag to compare

@childrentime childrentime released this 31 Jul 02:31

🐛 Fixes

useOrientation: lockOrientation / unlockOrientation were no-ops — closes #215

Both methods guarded with if (isBrowser) return — inverted. They returned early in the browser and only proceeded during SSR, where the follow-up 'screen' in window check bailed anyway. The result: calling lockOrientation('landscape') or unlockOrientation() silently did nothing, everywhere. The guards now read if (!isBrowser) return, and the PR adds browser + SSR specs so the direction can't flip again unnoticed.

Full PR: #215

useInterval: a manually resumed interval survived unmount — closes #212

Two leaks in controls mode:

  • With controls: true the main effect returns before registering a cleanup, so an interval started through resume() kept firing after the component unmounted — despite the docs promising it's cleared on unmount. The cleanup now runs in its own mount-scoped effect, covering both modes without changing the delay-update behavior.
  • resume() overwrote timer.current without clearing the previous timer, so calling it twice left an interval that neither pause() nor the unmount cleanup could reach. It now clears any running timer before starting a new one.

Full PR: #212

useMicrophone: level stayed frozen after stop() — closes #213

teardownAudioGraph() cancels the rAF loop, which is the only writer of level, so after stop() the value froze at whatever the last frame measured. A volume meter bound to it kept showing input long after the microphone was released. stop() now resets level to 0.

Full PR: #213

useElementByPoint: no more re-render on every frame in multiple mode — closes #214

document.elementsFromPoint() allocates a fresh array on every call, so storing its result as-is re-rendered the component on every frame of the rAF loop — even with the pointer sitting still. The hit list is now compared element-by-element inside a functional update, and the previous state is kept when nothing changed. The single-element mode needed no change: elementFromPoint() returns the same node and React bails out on its own.

Full PR: #214

Thanks @ostapondo for all four reports and fixes.

🔧 Internal

  • CI now runs a dedicated typecheck gate (tsc --noEmit), and TypeScript versions are aligned across the workspace (#211). As part of it, useGeolocation's placeholder coords gained the toJSON that GeolocationCoordinates carries in lib.dom.

v6.4.1

Choose a tag to compare

@childrentime childrentime released this 28 Jul 16:03

🐛 Fixes

useScriptTag no longer emits an unhandled promise rejection — closes #206

The immediate auto-load called load() as a bare statement, so nothing was attached to the promise it returns. When the script failed — blocked by an ad blocker, offline, 404 — the error listener rejected that promise with no handler, and the rejection reached window.onunhandledrejection, where error trackers reported it. Setting status to 'error' does not mark a promise handled; setStatus and reject are independent paths.

Any useScriptTag pointing at analytics, a chat widget, or a third-party SDK hit this for every user running a blocklist.

The auto-load now attaches a no-op catch:

  • status === 'error' remains the reporting channel for a load nobody awaited.
  • Callers that hold the promise themselves are unaffected — load() memoizes into _promise.current, so an explicit load() returns the same promise and still rejects for them.

Thanks @Faithfinder for the report and the fix.

Full PR: #206

v6.4.0

Choose a tag to compare

@childrentime childrentime released this 25 Jun 11:47

✨ Features

Same-tab component sync for storage & cookie hooks — closes #202

Two components bound to the same key in the same tab now stay in sync. The native storage event only fires in other tabs, so previously a header and footer useColorMode (or two useCookie on the same key) never updated each other.

  • useLocalStorage / useSessionStorage (createStorage): each write re-broadcasts a custom window event that sibling instances pick up via useEventListener; cross-tab sync still rides the native storage event.
  • useCookie: same primitive — cookies fire no native event. refreshCookie stays for changes made outside the hook.
  • useColorMode / useDarkMode: inherit it for free (built on createStorage).

Notes:

  • Uses a window event (not a module-level registry) so it survives the library being bundled more than once.
  • listenToStorageChanges now gates the cross-tab listener only; same-tab sync is always on.

🐛 Fixes

  • useTimeout / useTimeoutFn no longer flash pending false → true on mount — it seeds the real armed value from immediate. Closes #203.

📖 Docs

Corrected the useCookie / useLocalStorage / useSessionStorage sync notes and added clickable two-component live demos (en + zh-Hans + zh-Hant).

Full PR: #204

v6.3.2

Choose a tag to compare

@childrentime childrentime released this 22 May 01:31

Bug Fixes

  • core: emit default values in the generated API docs. Five hooks used the non-standard @default JSDoc tag, which the doc generator silently dropped (rendering the default column as -). Switched to the TSDoc-standard @defaultValue tag so defaults now show correctly.
    • Affected hooks: useMicrophone, useElementBounding, useScroll, useScratch, useSpeechRecognition (and useInfiniteScroll, which reuses the useScroll options).

Docs

  • Add the useWakeLock API reference, which was never generated/committed when the hook landed (#194), so its docs page no longer renders an empty API table.

Note: runtime bundle is unchanged from v6.3.1 — this release only corrects JSDoc/type metadata (.d.ts) and the generated documentation tables.

v6.3.1

Choose a tag to compare

@childrentime childrentime released this 01 Apr 03:57

Bug Fixes

  • useClipboard: fix SSR safety issue — replace direct window reference with defaultWindow in focus event listener to prevent ReferenceError in server-side rendering environments

v6.3.0

Choose a tag to compare

@childrentime childrentime released this 24 Mar 15:26

What's Changed

Refactor

  • createStorage: Replace useState + useDeepCompareEffect with useSyncExternalStore, eliminating CSR first-render flicker, SSR hydration mismatches, and stale cross-tab reads (#195)

Bug Fixes

  • createStorage: Fix stale closure in consecutive functional updates within the same synchronous batch — updateState now reads from getSnapshot() instead of the render-time state

Tests

  • useLocalStorage: Add 12 new test cases covering cross-tab sync, storage.clear(), listenToStorageChanges toggle, three-state semantics, onError, mountStorageValue, and consecutive functional updates

Full Changelog: v6.1.12...v6.3.0

v6.1.12

Choose a tag to compare

@childrentime childrentime released this 10 Mar 08:42
  • fix(useGeolocation): make useSupported check more robust

6.1.2(Oct 30, 2025)

Choose a tag to compare

@childrentime childrentime released this 30 Oct 06:13
  • feat: add useScratch hook.

6.1.0(Sep 24, 2025)

Choose a tag to compare

@childrentime childrentime released this 24 Sep 09:33

Breaking Changes

  • useDarkMode: Storage format changed from boolean to string values ('dark'/'light'). This affects localStorage/sessionStorage data and SSR scripts. Existing stored boolean values will be automatically migrated, but custom SSR scripts need to be updated to handle string comparisons instead of boolean values.

Core

  • feat: add useMap hook for reactive Map state management with set, get, remove, has, clear, and reset operations
  • feat: add useColorMode hook with support for multiple color modes beyond dark/light
  • feat: add useBoolean hook for boolean state management with setValue, setTrue, setFalse, and toggle operations
  • feat: add useClickAway alias for useClickOutside hook
  • feat: add useCopyToClipboard alias for useClipboard hook
  • feat: add comprehensive documentation for useColorMode hook
  • docs: add context provider examples for both useColorMode and useDarkMode
  • docs: add multi-color theme examples in useColorMode documentation (6 themes: light, dark, blue, green, purple, sepia)
  • docs: update useDarkMode documentation scripts to handle new string storage format