From b07cf67d9eb1e484ec9dbda21560b2015137030c Mon Sep 17 00:00:00 2001
From: Ashish Prajapati <62009244+Ashish-simpleCoder@users.noreply.github.com>
Date: Sat, 28 Dec 2024 11:01:51 +0000
Subject: [PATCH 01/81] docs: revamp the docs for readme and guideline
---
CONTRIBUTING.md | 20 ++++++++++++++---
README.md | 57 ++++++++++++++++++++++++++++++++-----------------
2 files changed, 54 insertions(+), 23 deletions(-)
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 7580c6e..a558196 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -2,7 +2,21 @@
Hi! We are really excited that you are interested in contributing to classic-react-hooks. Before submitting your contribution, please make sure to take a moment and read through the following guide:
-## Repo Setup
+
+
+## 🔧 System Requirements
+- Node.js v16 or higher
+- Pnpm v8 or higher
+
+
+## 🏗️ Repo Setup
+
+- Clone the repository:
+```bash
+git clone https://github.com/Ashish-simpleCoder/classic-react-hooks.git
+
+cd classic-react-hooks
+```
The package manager used to install and link dependencies should be [pnpm](https://pnpm.io/) v8.12.0 or higher. NodeJS version should be v18.14.2 or higher
@@ -14,7 +28,7 @@ The package manager used to install and link dependencies should be [pnpm](https
4. Run `pnpm run format` to format all of the coding with prettier
-## Pull Request Guidelines
+## 🔃 Pull Request Guidelines
- Checkout a topic branch from a base branch, e.g. `main`, and merge back against that branch.
@@ -35,7 +49,7 @@ The package manager used to install and link dependencies should be [pnpm](https
- Use `pnpm format` to format files according to the project guidelines.
-## Documenation Guidelines
+## 📄 Documenation Guidelines
- To contribute in the documentation, go to apps/doc directory
diff --git a/README.md b/README.md
index 537715b..33a04c2 100644
--- a/README.md
+++ b/README.md
@@ -1,10 +1,9 @@
# 🚀 classic-react-hooks
-#### An awesome collection of `feature packed custom hooks`.
+An awesome collection of `feature` packed custom hooks.
-
+ )
+}
+```
diff --git a/apps/doc/hooks/use-debounced-fn.md b/apps/doc/hooks/use-debounced-fn.md
index d225959..d2e1042 100644
--- a/apps/doc/hooks/use-debounced-fn.md
+++ b/apps/doc/hooks/use-debounced-fn.md
@@ -4,38 +4,100 @@ outline: deep
# use-debouced-fn
-- A hook which returns a debounced function.
+- A React hook that returns a debounced version of any function, delaying its execution until after a specified delay has passed since the last time it was invoked.
+- Perfect for optimizing performance in scenarios like search inputs, API calls, or resize handlers.
+
+### Features
+
+- **Debouncing Functionality:** The primary feature is delaying function execution until after a specified period of inactivity. If the function is called again before the delay expires, the previous call is cancelled and the timer resets.
+- **Configurable Delay:** You can specify a custom delay period, with a sensible default of 300ms if none is provided.
+- **Dynamic Props Updates:** The hook properly handles updates to both the callback function and delay value during re-renders without losing the debouncing behavior.
+- **Performance optimized:** Prevents excessive function calls
+- **Auto cleanup:** Automatically clears timers on component unmount and on delay prop change
### Parameters
-| Parameter | Type | Required | Default Value | Description |
-| --------- | :------: | :------: | :-----------: | ----------------------------------------------------------- |
-| cb | Function | ✅ | - | A callback which is to be debounced. |
-| delay | number | ❌ | 300 | A delay in milliseconds after that the callback gets fired. |
+| Parameter | Type | Required | Default Value | Description |
+| ---------------- | :------: | :------: | :-----------: | ----------------------------------------------------- |
+| callbackToBounce | Function | ✅ | - | The function to be debounced |
+| delay | number | ❌ | 300 | Delay in milliseconds before the function is executed |
### Returns
-- It returns a function which is debouced version of passed callback.
+- Returns a debounced version of the provided function that will only execute after the specified delay has passed since the last invocation.
-### Usage
+### Usage Examples
+
+#### Basic debouncing
```ts
-import { useState } from 'react'
+import { useState, useEffect } from 'react'
import { useDebouncedFn } from 'classic-react-hooks'
-export default function YourComponent() {
- const [debouncedInput, setDebouncedInput] = useState('')
- const updateInput = useDebouncedFn((e) => {
- setDebouncedInput(e.target.value)
- }, 300)
+export default function SearchInput() {
+ const [query, setQuery] = useState('')
+ const [results, setResults] = useState([])
+
+ const debouncedSearch = useDebouncedFn({
+ callbackToBounce: async (searchTerm: string) => {
+ if (searchTerm.trim()) {
+ const response = await fetch(`https://dummyjson.com/users/search?q=${searchTerm}`)
+ const data = await response.json()
+ setResults(data.results)
+ }
+ },
+ delay: 500,
+ })
+
+ const handleInputChange = (e: React.ChangeEvent) => {
+ const value = e.target.value
+ setQuery(value)
+ debouncedSearch(value)
+ }
+
+ useEffect(() => {
+ ;(async function () {
+ const response = await fetch(`https://dummyjson.com/users`)
+ const data = await response.json()
+ setResults(data.results)
+ })()
+ }, [])
return (
-
-
- value - {debouncedInput}
-
+
+
+ {results.map((result) => (
+
{result.name}
+ ))}
+
)
}
```
+
+### Common Use Cases
+
+- Delay API calls until user stops typing
+- Validate fields after user pauses input
+- Prevent excessive API calls
+
+### Alternative: Non-React Usage
+
+For use outside of React components, use the standalone wrapper:
+
+```ts
+import { debouncedFnWrapper } from 'classic-react-hooks'
+
+const { fn: debouncedLog, cleanup } = debouncedFnWrapper({
+ callbackToBounce: (message: string) => console.log(message),
+ delay: 1000,
+})
+
+// Use the debounced function
+debouncedLog('Hello')
+debouncedLog('World') // Only 'World' will be logged after 1 second
+
+// Clean up when done
+cleanup()
+```
diff --git a/apps/doc/hooks/use-event-listener.md b/apps/doc/hooks/use-event-listener.md
index d52c965..569654a 100644
--- a/apps/doc/hooks/use-event-listener.md
+++ b/apps/doc/hooks/use-event-listener.md
@@ -4,45 +4,114 @@ outline: deep
# use-event-listener
-- A hook which handles dom events in efficient and declarative manner.
+A React hook that provides a declarative way to add DOM event listeners with automatic cleanup.
-### Parameters
+### Features
-| Parameter | Type | Required | Default Value | Description |
-| --------- | :-----------------------: | :------: | :-----------: | ------------------------------ |
-| target | [Target](#parametertype) | ✅ | - | Reference of the html element |
-| event | string | ✅ | - | Event name |
-| handler | [Handler](#parametertype) | ❌ | undefined | Callback for the event |
-| options | [Options](#parametertype) | ❌ | undefined | For managing Event Propagation |
+- **Auto cleanup:** Events are automatically removed on unmount or dependency changes
+- **Reactive:** The hook re-evaluates and potentially re-attaches listeners when any dependency changes (target, event, options)
+- **Conditional events:** Built-in support for conditionally enabling/disabling event
+- **Performance:** Event listeners are only attached when all conditions are met: target exists, handler is provided, and `shouldInjectEvent` is true
+- **Standard options:** Full support for all `AddEventListenerOptions` (capture, once, passive, signal)
-### Types
+### Parameters
----
+| Parameter | Type | Required | Default Value | Description |
+| --------- | :-----------------: | :------: | :-----------: | ------------------------------------------------ |
+| target | [EvTarget](#types) | ✅ | - | Function that returns the target element or null |
+| event | string | ✅ | - | Event name (e.g., 'click', 'keydown', 'resize') |
+| handler | [EvHandler](#types) | ❌ | undefined | Event handler callback function |
+| options | [EvOptions](#types) | ❌ | undefined | Event listener options and feature flags |
+| |
-#### ParameterType
+#### Types
```ts
-type Target = null | EventTarget | (() => EventTarget | null)
-type Options = boolean | (AddEventListenerOptions & { shouldInjectEvent?: boolean | any })
-type Handler = (event: Event) => void
+export type EvTarget = () => EventTarget | null
+export type EvHandler = (event: Event) => void
+
+export interface EvOptions extends AddEventListenerOptions {
+ // Standard AddEventListenerOptions:
+ // capture?: boolean
+ // once?: boolean
+ // passive?: boolean
+ // signal?: AbortSignal
+
+ // Custom option:
+ shouldInjectEvent?: boolean | any // Controls whether the event should be attached
+}
```
-### Usage
+### Usage Examples
+
+#### Basic Click Handler
```ts
import { useRef } from 'react'
import { useEventListener } from 'classic-react-hooks'
-export default function YourComponent() {
- const ref = useRef()
- useEventListener(ref, 'click', (e) => {
- console.log(e)
+export default function ClickExample() {
+ const buttonRef = useRef(null)
+
+ useEventListener({
+ target: () => buttonRef.current,
+ event: 'click',
+ handler: (e) => {
+ console.log('Button clicked!', e)
+ },
+ })
+
+ return
+}
+```
+
+#### Window Events
+
+```ts
+import { useEventListener } from 'classic-react-hooks'
+
+export default function WindowExample() {
+ useEventListener({
+ target: () => window,
+ event: 'resize',
+ handler: (e) => {
+ console.log('Window resized:', window.innerWidth, window.innerHeight)
+ },
+ })
+
+ return
)
}
```
+
+### Common Use Cases
+
+- Adding dom events (e.g 'click', 'keydown', 'resize')
diff --git a/apps/doc/hooks/use-intersection-observer.md b/apps/doc/hooks/use-intersection-observer.md
index 5636ece..de0e1e9 100644
--- a/apps/doc/hooks/use-intersection-observer.md
+++ b/apps/doc/hooks/use-intersection-observer.md
@@ -4,77 +4,233 @@ outline: deep
# use-intersection-observer
-- A hook which provides a way for listening to the Intersection Observer event for given target.
-- It returns an array of boolean values which represents whether the targets are intersecting the screen or not.
+A React hook that provides a declarative way to observe multiple elements with the Intersection Observer API, returning their visibility states with advanced triggering options.
+
+### Features
+
+- **Multiple targets:** Observe multiple elements simultaneously
+- **Flexible triggering:** Control whether elements trigger once or continuously
+- **Per-element configuration:** Different trigger behavior for each element
+- **Auto cleanup:** Observer is automatically disconnected on unmount
+- **Fallback support:** Graceful degradation when IntersectionObserver is not available
+- **Callback support:** Execute custom logic when elements become visible
+- **Performance:** Elements with `only_trigger_once: true` are automatically unobserved after first intersection
+- **Per-element control:** Use `only_trigger_once` as an array to control trigger behavior per element
### Parameters
-| Parameter | Type | Required | Default Value | Description |
-| --------- | :------------------------: | :------: | :-----------: | ------------------------------------------------------------- |
-| targets | [Target[]](#parametertype) | ✅ | - | Array of targets which contains reference of the html element |
-| options | [Options](#parametertype) | ❌ | {} | Options to pass as feature flag |
+| Parameter | Type | Required | Default | Description |
+| -------------- | :------------------------------------: | :------: | :---------------------------: | ------------------------------------------------------- |
+| targets | [IntersectionObserverTarget[]](#types) | ✅ | - | Array of functions that return target elements |
+| options | [IntersectionOptions](#types) | ❌ | "{ only_trigger_once: true }" | Intersection observer options and custom configurations |
+| onIntersection | (target: Element) => void | ❌ | undefined | Callback executed when an element becomes visible |
+| |
-### Types
+#### Types
----
+```ts
+export type IntersectionObserverTarget = () => Element | null
+export type IsTargetIntersecting = boolean
+
+export interface IntersectionOptions extends IntersectionObserverInit {
+ // Standard IntersectionObserverInit:
+ // root?: Element | Document | null
+ // rootMargin?: string
+ // threshold?: number | number[]
+
+ // Custom options
+ only_trigger_once?: boolean | boolean[] // Control per-element trigger behavior
+}
+```
+
+### Return Value
+
+Returns an array of boolean values (`Array`) where each boolean represents whether the corresponding target element is currently intersecting (visible) or not.
-#### ParameterType
+### Usage Examples
+
+#### Basic Usage - Multiple Elements
```ts
-type Target = HTMLElement | RefObject | (() => HTMLElement | null) | null
-type Options = {
- mode?: 'lazy' | 'virtualized'
-} & IntersectionObserverInit
+import { useRef } from 'react'
+import { useInterSectionObserver } from 'classic-react-hooks'
+
+export default function BasicIntersection() {
+ const box1Ref = useRef(null)
+ const box2Ref = useRef(null)
+ const box3Ref = useRef(null)
+
+ const [isBox1Visible, isBox2Visible, isBox3Visible] = useInterSectionObserver({
+ targets: [() => box1Ref.current, () => box2Ref.current, () => box3Ref.current],
+ })
+
+ return (
+
- Lorem ipsum dolor sit amet consectetur adipisicing elit. Modi quae illum rem quod recusandae a tempora
- officia natus quos dignissimos, eum beatae ea! Consectetur nemo assumenda eligendi optio voluptatum fuga.
-
-
- Lorem ipsum dolor sit amet consectetur adipisicing elit. Modi quae illum rem quod recusandae a tempora
- officia natus quos dignissimos, eum beatae ea! Consectetur nemo assumenda eligendi optio voluptatum fuga.
-
+ )
+}
+```
+
+### Important Notes
+
+- If IntersectionObserver is not supported, a warning is logged and the hook gracefully degrades.
+
+### Common Use Cases
+
+- Lazy loading images or content
+- Triggering animations on scroll
+- Analytics tracking for element visibility
+- Infinite scrolling implementation
+- Performance optimization by conditionally rendering components
+- Scroll-triggered navigation highlighting
diff --git a/apps/doc/hooks/use-interval-effect.md b/apps/doc/hooks/use-interval-effect.md
index d572d7a..5c3ecb6 100644
--- a/apps/doc/hooks/use-interval-effect.md
+++ b/apps/doc/hooks/use-interval-effect.md
@@ -4,39 +4,59 @@ outline: deep
# use-interval-effect
-- A hooks which fires the provided callback every time when the given delay is passed, just like the `setInterval`.
+A React hook that executes a callback function at regular intervals, similar to `setInterval` but with additional control methods for clearing and restarting the timer.
+
+### Features
+
+- **Scheduled execution:** Executes a callback with a fixed time delay between each call
+- **Flexible:** Provides methods to clear or restart the timer
+- **Automatic Cleanup:** Automatically cleans up timer on component unmount
+- **Syncronization:** Syncs with the latest callback and timeout values
### Parameters
-| Parameter | Type | Required | Default Value | Description |
-| --------- | :------: | :------: | :-----------: | -------------------------------------------------------------------- |
-| cb | Function | ✅ | - | Callback gets fired after every given amount of interval is passed . |
-| interval | number | ❌ | 100 | Interval value after which the callback is fired. |
+| Parameter | Type | Required | Default Value | Description |
+| --------- | :------: | :------: | :-----------: | ------------------------------------------------ |
+| handler | Function | ✅ | - | Callback function executed at each interval |
+| interval | number | ❌ | 100 | Time in milliseconds between callback executions |
### Returns
-- It returns an object.
-- `clearTimer` : () => void
-- `restartTimer` : () => void
+- Returns an object with control methods:
+
+ - `clearTimer` : `() => void` Cancels the current interval, preventing the handler from executing
+ - `restartTimer` : `() => void` Clears the current timer and starts a new one. Optionally accepts a new interval value
### Usage
+#### Basic example
+
```ts
import { useState } from 'react'
import { useIntervalEffect } from 'classic-react-hooks'
-export default function YourComponent() {
- const [counter, setCounter] = useState(0)
- const { clearTimer, restartTimer } = useIntervalEffect(() => {
- setCounter((c) => c + 1)
- }, 1000)
+export default function Counter() {
+ const [count, setCount] = useState(0)
+
+ const { clearTimer, restartTimer } = useIntervalEffect({
+ handler: () => setCount((prev) => prev + 1),
+ interval: 1000, // 1 second
+ })
return (
-
{counter}
-
-
+
Count: {count}
+
+
+
+
)
}
```
+
+### Common Use Cases
+
+- Countdown timers
+- Real-time updates (clocks, progress bars)
+- Polling APIs at regular intervals
diff --git a/apps/doc/hooks/use-is-online.md b/apps/doc/hooks/use-is-online.md
index b42297c..d8e2a4b 100644
--- a/apps/doc/hooks/use-is-online.md
+++ b/apps/doc/hooks/use-is-online.md
@@ -4,20 +4,34 @@ outline: deep
# use-is-online
-- A simple hook for getting the network connection state.
+A React hook that provides real-time network connection status using the browser's `navigator.onLine` API.
+
+### Features
+
+- **Real-time updates:** Real-time network status updates
+- **SSR safe:** SSR-safe with proper hydration handling
+- **Lightweight:** Lightweight with no external dependencies
+- **Core hook:** Built on React's useSyncExternalStore for optimal performance
### Returns
-- `connectionState` : boolean
+- `isOnline (boolean):` Current network connection state
+
+ - `true` when the browser is online
+ - `false` when the browser is offline
-### Usage
+### Usage Examples
+
+#### Basic Network query
```ts
import { useIsOnline } from 'classic-react-hooks'
-export default function YourComponent() {
+function NetworkStatus() {
const isOnline = useIsOnline()
- return
{isOnline ? 'online' : 'offline'}
+ return
Connection: {isOnline ? '🟢 Online' : '🔴 Offline'}
}
```
+
+### Important Notes
diff --git a/apps/doc/hooks/use-local-storage.md b/apps/doc/hooks/use-local-storage.md
index 4af8e56..0def9b3 100644
--- a/apps/doc/hooks/use-local-storage.md
+++ b/apps/doc/hooks/use-local-storage.md
@@ -4,49 +4,132 @@ outline: deep
# use-local-storage
-- A hook for managing the states with `local-storage`
-- It automatically updates the state in `local-storage`
-- It is `useState` with local storage power.
+- A React hook that synchronizes state with localStorage, providing `persistent` state management across browser `sessions`.
+- Works exactly like `useState` but automatically persists data to localStorage.
+
+### Features
+
+- **Automatic sync:** Automatic localStorage synchronization
+- **Persistence updates:** Seamless state updates with persistence
+- **Error handling:** Built-in error handling and fallbacks
+- **Compatible API:** useState-compatible API
+- **Automatic parsing:** JSON serialization/deserialization
### Parameters
-| Parameter | Type | Required | Default Value | Description |
-| ------------ | :----: | :------: | :-----------: | ---------------------------------------------------- |
-| key | string | ✅ | - | key for getting an item from local-storage |
-| defaultValue | any | ❌ | - | A initial value when item is not found local-storage |
+| Parameter | Type | Required | Default Value | Description |
+| ------------ | :----: | :------: | :-----------: | ----------------------------------------- |
+| key | string | ✅ | - | Unique key for localStorage item |
+| defaultValue | any | ❌ | undefined | Initial value when no stored value exists |
### Returns
-- It returns an array of `state` and setter function `setState`.
-- `state` : It's type get inferred by `defaultValue` parameter.
-- `setState` : A function just like the setter function from `useState`.
+- It returns an array containing:
+
+ 1. `state:` Current value (type inferred from defaultValue)
+ 2. `setState:` State setter function (identical to useState setter)
+
+### Usage Examples
-### Usage
+#### Basic User Preferences
```ts
import { useLocalStorage } from 'classic-react-hooks'
-export default function YourComponent() {
- const [user_details, setUserDetails] = useLocalStorage('user_details', {
- name: '',
- })
+function UserPreferences() {
+ const [theme, setTheme] = useLocalStorage({ key: 'theme', defaultValue: 'light' })
+ const [language, setLanguage] = useLocalStorage({ key: 'language', defaultValue: 'en' })
return (
makeActive(e.currentTarget)} className='p-5 bg-gray-100 m-2.5'>
+ Click to make active
)
}
```
+
+### Common Use Cases
+
+- Modal dialogs - Close when clicking backdrop
+- Dropdown menus - Hide when clicking elsewhere
+- Context menus - Dismiss on outside click
diff --git a/apps/doc/hooks/use-synced-effect.md b/apps/doc/hooks/use-synced-effect.md
index 9d2402c..11d85d0 100644
--- a/apps/doc/hooks/use-synced-effect.md
+++ b/apps/doc/hooks/use-synced-effect.md
@@ -4,17 +4,26 @@ outline: deep
# use-synced-effect
-- A hooks that fires the given callback for given dependencies when they get change.
-- It works exacatly like `useEffect`. But callback doesn't get fired on initial mount.
+- A React hook that executes a callback when dependencies change, similar to `useEffect`, but skips execution on the initial mount.
+- This is particularly useful when you want to respond to state changes without triggering side effects during the component's first render.
+
+### Features
+
+- **Skip initial mount:** Skipping the callback on initial mount
+- **Reactive:** Running the callback only when dependencies actually change
+- **React StrictMode:** Handling React StrictMode double execution correctly
+- **Flexible:** Supporting cleanup functions just like useEffect
### Parameters
-| Parameter | Type | Required | Default Value | Description |
-| --------- | :------: | :------: | :-----------: | ----------------------------------------------- |
-| cb | Function | ✅ | - | Callback to fire when dependencies get changed. |
-| deps | Array | ❌ | [] | Dependencies. |
+| Parameter | Type | Required | Default Value | Description |
+| --------- | :------------------: | :------: | :-----------: | ------------------------------------------------------------------------------------ |
+| cb | React.EffectCallback | ✅ | - | Callback function to execute when dependencies change. Can return a cleanup function |
+| deps | React.DependencyList | ❌ | [] | Array of dependencies to watch for changes |
+
+### Usage Examples
-### Usage
+#### Basic Usage - Responding to State Changes
```ts
import { useState } from 'react'
@@ -34,3 +43,54 @@ export default function YourComponent() {
)
}
```
+
+#### With Cleanup Function
+
+```ts
+import { useState } from 'react'
+import { useSyncedEffect } from 'classic-react-hooks'
+
+function SearchComponent() {
+ const [query, setQuery] = useState('')
+
+ useSyncedEffect(() => {
+ // Only search when query actually changes, not on initial empty string
+ if (query) {
+ const controller = new AbortController()
+
+ fetch(`/api/search?q=${query}`, {
+ signal: controller.signal,
+ })
+ .then((response) => response.json())
+ .then((data) => {
+ // Handle search results
+ })
+
+ // Cleanup function to cancel the request
+ return () => {
+ controller.abort()
+ }
+ }
+ }, [query])
+
+ return setQuery(e.target.value)} placeholder='Search...' />
+}
+```
+
+### Comparison with useEffect
+
+| Scenario | useEffect | useSyncedEffect |
+| ------------------- | ---------------- | -------------------- |
+| Initial mount | ✅ Runs | ❌ Skips |
+| Dependency changes | ✅ Runs | ✅ Runs |
+| Cleanup support | ✅ Yes | ✅ Yes |
+| StrictMode handling | ⚠️ May run twice | ✅ Handles correctly |
+
+### Important notes
+
+- Empty dependency array [] means the effect will never run (since there are no dependencies to change)
+- No dependency array means the effect will never run (same as array [])
+
+### Common Use Cases
+
+- Use everywhere just like `useEffect`
diff --git a/apps/doc/hooks/use-synced-ref.md b/apps/doc/hooks/use-synced-ref.md
index 6dd2059..c61089e 100644
--- a/apps/doc/hooks/use-synced-ref.md
+++ b/apps/doc/hooks/use-synced-ref.md
@@ -4,34 +4,100 @@ outline: deep
# use-synced-ref
-- A replacement for `useRef` hook, which automatically syncs-up with the given state.
-- No need to manually update the ref.
+- A React hook that creates a ref that automatically stays in sync with the provided value.
+- This eliminates the need to manually update refs and helps avoid stale closure issues in callbacks and effects.
+
+### Features
+
+- **Reactive:** Automatic synchronization with any value
+- **Prevent State Closure:**Prevents stale closure problems
+- **No Re-render:**Zero re-renders - purely ref-based
### Parameters
-| Parameter | Type | Required | Default Value | Description |
-| --------- | :--: | :------: | :-----------: | --------------------------- |
-| value | any | ✅ | - | Value to be tracked in ref. |
+| Parameter | Type | Required | Default Value | Description |
+| --------- | :--: | :------: | :-----------: | ------------------------------------------------- |
+| value | any | ✅ | - | Any value to be tracked and kept in sync with ref |
### Returns
-- It returns the ref of given state.
+- Returns a `React.MutableRefObject` that always contains the latest value of the provided state.
### Usage
+#### Basic Example
+
```ts
import { useState } from 'react'
import { useSyncedRef } from 'classic-react-hooks'
-export default function YourComponent() {
- const [counter, setCounter] = useState(0)
+export default function Counter() {
+ const [count, setCount] = useState(0)
+ const countRef = useSyncedRef(count)
- const counterRef = useSyncedRef(counter)
+ const handleAsyncOperation = () => {
+ setTimeout(() => {
+ // countRef.current always has the latest value
+ console.log('Current count:', countRef.current)
+ alert(`Count is now: ${countRef.current}`)
+ }, 2000)
+ }
return (
-
+
Count: {count}
+
+
)
}
```
+
+### Problem It Solves
+
+#### The Stale Closure Problem
+
+In React, when you use hooks like useEffect, useCallback, or setTimeout with dependency arrays, you often encounter stale closure issues:
+
+```ts
+// ❌ Problematic code
+function ProblematicComponent() {
+ const [count, setCount] = useState(0)
+
+ useEffect(() => {
+ const interval = setInterval(() => {
+ console.log(count) // Always logs 0 (stale closure)
+ }, 1000)
+ return () => clearInterval(interval)
+ }, []) // Empty deps = stale closure
+
+ // vs
+
+ useEffect(() => {
+ const interval = setInterval(() => {
+ console.log(count) // Works but recreates interval on every count change
+ }, 1000)
+ return () => clearInterval(interval)
+ }, [count]) // Including count fixes staleness but causes recreation
+}
+
+// ✅ Solution with useSyncedRef
+function SolvedComponent() {
+ const [count, setCount] = useState(0)
+ const countRef = useSyncedRef(count)
+
+ useEffect(() => {
+ const interval = setInterval(() => {
+ console.log(countRef.current) // Always logs latest count
+ }, 1000)
+ return () => clearInterval(interval)
+ }, []) // Empty deps = no recreation, no staleness!
+}
+```
+
+### Common Use Cases
+
+- Accessing latest state in intervals/timeouts
+- Event handlers that need current state
+- Custom hooks with complex state dependencies
+- Preventing effect recreations while avoiding stale closures
diff --git a/apps/doc/hooks/use-throttled-fn.md b/apps/doc/hooks/use-throttled-fn.md
index acca427..8e792bd 100644
--- a/apps/doc/hooks/use-throttled-fn.md
+++ b/apps/doc/hooks/use-throttled-fn.md
@@ -4,38 +4,96 @@ outline: deep
# use-throttled-fn
-- A hook which returns a throttled function.
+A React hook that returns a throttled version of a callback function.
+
+- Throttling ensures that the function is called at most once per specified time interval, regardless of how many times it's invoked.
+- This is particularly useful for performance optimization in scenarios like handling rapid user input, scroll events, or API calls.
+
+### Features
+
+- **Throttling Functionality:** Limits function execution to at most once per specified time period
+- **Configurable Delay:** Accepts a custom delay period with a default of 300ms
+- **Dynamic Props Updates:** The hook properly handles updates to both the callback function and delay value during re-renders without losing the debouncing behavior
+- **Performance optimized:** Prevents excessive function calls
### Parameters
-| Parameter | Type | Required | Default Value | Description |
-| --------- | :------: | :------: | :-----------: | ----------------------------------------------------------- |
-| cb | Function | ✅ | - | A callback which is to be throttled. |
-| delay | number | ❌ | 300 | A delay in milliseconds after that the callback gets fired. |
+| Parameter | Type | Required | Default Value | Description |
+| ------------------ | :------: | :------: | :-----------: | --------------------------------------------------------- |
+| callbackToThrottle | Function | ✅ | - | The callback function that should be throttled |
+| delay | number | ❌ | 300 | Delay in milliseconds between allowed function executions |
### Returns
-- It returns a function which is throttled version of passed callback.
+- Returns a throttled version of the provided callback function that maintains the same signature and behavior, but with throttling applied.
+
+### Usage Examples
-### Usage
+#### Basic API throttling
```ts
import { useState } from 'react'
import { useThrottledFn } from 'classic-react-hooks'
-export default function YourComponent() {
- const [throttledInput, setThrottledInput] = useState('')
- const updateInput = useThrottledFn((e) => {
- setThrottledInput(e.target.value)
- }, 300)
+export default function AutoSave() {
+ const [content, setContent] = useState('')
+ const [saving, setSaving] = useState(false)
+
+ const saveContent = useThrottledFn({
+ callbackToThrottle: async (text) => {
+ setSaving(true)
+ try {
+ await saveToAPI(text)
+ console.log('Content saved!')
+ } catch (error) {
+ console.error('Save failed:', error)
+ } finally {
+ setSaving(false)
+ }
+ },
+ delay: 2000, // Auto-save every 2 seconds at most
+ })
+
+ const handleChange = (e) => {
+ const newContent = e.target.value
+ setContent(newContent)
+ saveContent(newContent)
+ }
return (
-
-
- value - {throttledInput}
-
+
+ {saving &&
Saving...
}
)
}
```
+
+### Common Use Cases
+
+- Real-time form validation
+- Prevent excessive API calls
+- Throttle scroll event processing
+
+### Alternative: Non-React Usage
+
+For use outside of React components, use the standalone wrapper:
+
+```ts
+import { throttledFnWrapper } from 'classic-react-hooks'
+
+const throttledLog = throttledFnWrapper({
+ callbackToThrottle: (message: string) => console.log(message),
+ delay: 1000,
+})
+
+// Use the throttled function
+throttledLog('Hello') // will log "Hello" in console
+throttledLog('World') // ignored till 1 second is not passed
+throttledLog('1') // ignored till 1 second is not passed
+throttledLog('2') // ignored till 1 second is not passed
+throttledLog('3') // ignored till 1 second is not passed
+// block for 1 second
+await sleep(1000)
+throttledLog('4') // will log "4" in console
+```
diff --git a/apps/doc/hooks/use-timeout-effect.md b/apps/doc/hooks/use-timeout-effect.md
index 2e19336..1c18af1 100644
--- a/apps/doc/hooks/use-timeout-effect.md
+++ b/apps/doc/hooks/use-timeout-effect.md
@@ -4,35 +4,52 @@ outline: deep
# use-timeout-effect
-- A hooks which fires the provided callback only once when the given delay is passed, just like the `setTimeout`.
+A React hook that fires a provided callback after a specified timeout, similar to `setTimeout`, but with additional control methods for clearing and restarting the timer.
+
+### Features
+
+- **Scheduled execution:** Executes a callback after a specified delay
+- **Flexible:** Provides methods to clear or restart the timer
+- **Automatic Cleanup:** Automatically cleans up timer on component unmount
+- **Syncronization:** Syncs with the latest callback and timeout values
### Parameters
-| Parameter | Type | Required | Default Value | Description |
-| --------- | :------: | :------: | :-----------: | --------------------------------------------------------- |
-| cb | Function | ✅ | - | Callback to fire after given amount of timeout is passed. |
-| timeout | number | ❌ | 100 | Timeout value after which the callback is fired. |
+| Parameter | Type | Required | Default Value | Description |
+| --------- | :------: | :------: | :-----------: | ------------------------------------------------------ |
+| handler | Function | ✅ | - | The callback function to execute after the timeout |
+| timeout | number | ❌ | 100 | The delay in milliseconds before executing the handler |
### Returns
-- It returns an object.
-- `clearTimer` : () => void
-- `restartTimer` : () => void
+- Returns an object with control methods:
+
+ - `clearTimer` : `() => void` Cancels the current timeout, preventing the handler from executing
+ - `restartTimer` : `() => void` Clears the current timer and starts a new one. Optionally accepts a new timeout value
-### Usage
+### Usage Examples
+
+#### Basic use
```ts
import { useState } from 'react'
-import { useTimeoutEffect } from 'classic-react-hooks'
+import useTimeoutEffect from './useTimeoutEffect'
-export default function YourComponent() {
- const [show, setShow] = useState(false)
+export default function BasicExample() {
+ const [message, setMessage] = useState('')
- useTimeoutEffect(() => {
- console.log('use-timeout-callback')
- setShow(true)
- }, 2000)
+ useTimeoutEffect({
+ handler: () => {
+ setMessage('Timer executed!')
+ },
+ timeout: 2000,
+ })
- return
{show &&
show
}
+ return
{message}
}
```
+
+### Important Notes
+
+- The hook uses `useSyncedRef` to ensure the latest callback and timeout values are always used.
+- The restartTimer method can accept an optional new timeout value, otherwise it uses the original timeout.
diff --git a/apps/doc/hooks/use-window-resize.md b/apps/doc/hooks/use-window-resize.md
index 522c6cd..68be94c 100644
--- a/apps/doc/hooks/use-window-resize.md
+++ b/apps/doc/hooks/use-window-resize.md
@@ -4,48 +4,129 @@ outline: deep
# use-window-resize
-- A hook which evaluates the passed callback on window resize event and returns the result of that callback.
+- A React hook that evaluates a callback function on window resize events and returns the result.
+- Perfect for responsive behavior based on window dimensions.
+
+### Features
+
+- **Custom handler** Execute custom logic on window resize
+- **Reactive** Automatic re-evaluation and state updates
+- **Configurable** Configurable default values and event injection
+- **Underlying hook** At its core, it uses `useEventListener` hook
### Parameters
-| Parameter | Type | Required | Default Value | Description |
-| --------- | :------------------------: | :------: | :-----------: | ------------------------------------------------------------------- |
-| cb | [Callback](#parametertype) | ✅ | - | A callback which gets fired whenever the window resize event occurs |
-| options | [Options](#parametertype) | ❌ | undefined | Options to pass as feature flag |
+| Parameter | Type | Required | Default Value | Description |
+| --------- | :---------------: | :------: | :-----------: | ------------------------------------------- |
+| handler | [Handler](#types) | ✅ | - | Callback function executed on window resize |
+| options | [Options](#types) | ❌ | undefined | Configuration options |
+| |
-#### ParameterType
+#### Types
```ts
-type Callback = () => T
-type Options = { shouldInjectEvent?: boolean; defaultValue?: T }
+type Handler = () => T
+type Options = { shouldInjectEvent?: boolean; defaultValue?: T }
```
### Returns
-- It returns the value, which is the result of the passed callback.
+- Returns the current result of the `handler` function, updated whenever the window is resized.
+
+### Usage Examples
-### Usage
+#### Basic Responsive Breakpoints
```ts
import { useWindowResize } from 'classic-react-hooks'
-export default function YourComponent() {
- const result = useWindowResize(() => {
- if (window.innerWidth < 500) {
- return 'md'
- }
- if (window.innerWidth > 500 && window.innerWidth < 800) {
- return 'lg'
- }
+function ResponsiveComponent() {
+ const breakpoint = useWindowResize(() => {
+ const width = window.innerWidth
+ if (width < 640) return 'sm'
+ if (width < 768) return 'md'
+ if (width < 1024) return 'lg'
return 'xl'
})
return (
- <>
- {result == 'md' &&
+ )
+ }
*
* @see Docs https://classic-react-hooks.vercel.app/hooks/use-intersection-observer.html
*/
diff --git a/src/lib/use-interval-effect/index.tsx b/src/lib/use-interval-effect/index.tsx
index 1a87428..7e4af32 100644
--- a/src/lib/use-interval-effect/index.tsx
+++ b/src/lib/use-interval-effect/index.tsx
@@ -4,8 +4,31 @@ import useSyncedRef from '../use-synced-ref'
/**
* @description
- * A hook which fires the provided callback every time when the given interval is passed, just like the setInterval.
- *
+ * A React hook that executes a callback function at regular intervals, similar to `setInterval` but with additional control methods for clearing and restarting the timer.
+ *
+ * @example
+ import { useState } from 'react'
+ import { useIntervalEffect } from 'classic-react-hooks'
+
+ export default function Counter() {
+ const [count, setCount] = useState(0)
+
+ const { clearTimer, restartTimer } = useIntervalEffect({
+ handler: () => setCount((prev) => prev + 1),
+ interval: 1000, // 1 second
+ })
+
+ return (
+
+
Count: {count}
+
+
+
+
+
+ )
+ }
+ *
* @see Docs https://classic-react-hooks.vercel.app/hooks/use-interval-effect.html
*/
export default function useIntervalEffect({ handler, interval = 100 }: { handler: () => void; interval?: number }) {
diff --git a/src/lib/use-local-storage/index.tsx b/src/lib/use-local-storage/index.tsx
index 826732a..14e9ce5 100644
--- a/src/lib/use-local-storage/index.tsx
+++ b/src/lib/use-local-storage/index.tsx
@@ -4,12 +4,30 @@ import React, { useRef, useState } from 'react'
/**
* @description
- * A hook for managing the states with `local-storage`.
- *
- * It working is just like the `useState`.
- *
- * It automatically updates the state in `local-storage`.
+ * A React hook that synchronizes state with localStorage, providing `persistent` state management across browser `sessions`.
*
+ * @example
+ import { useLocalStorage } from 'classic-react-hooks'
+
+ function UserPreferences() {
+ const [theme, setTheme] = useLocalStorage({ key: 'theme', defaultValue: 'light' })
+ const [language, setLanguage] = useLocalStorage({ key: 'language', defaultValue: 'en' })
+
+ return (
+
+
+
+
+
+ )
+ }
*
* @see Docs https://classic-react-hooks.vercel.app/hooks/use-local-storage.html
*/
diff --git a/src/lib/use-on-mount-effect/index.tsx b/src/lib/use-on-mount-effect/index.tsx
index 18faf98..0f1c1a4 100644
--- a/src/lib/use-on-mount-effect/index.tsx
+++ b/src/lib/use-on-mount-effect/index.tsx
@@ -4,10 +4,17 @@ import React, { useEffect } from 'react'
/**
* @description
- * A hooks that fires the given callback only once after the mount.
- *
- * It doesn't take any dependencies.
+ * A React hook that executes a callback function only once after the component mounts. This is a simplified wrapper around useEffect with an empty dependency array.
*
+ * @example
+ import { useOnMountEffect } from 'classic-react-hooks'
+ export default function YourComponent() {
+ useOnMountEffect(() => {
+ console.log('initial mount')
+ })
+
+ return
+ }
*
* @see Docs https://classic-react-hooks.vercel.app/hooks/use-on-mount-effect.html
*/
diff --git a/src/lib/use-outside-click/index.tsx b/src/lib/use-outside-click/index.tsx
index e98981b..c2c1b5e 100644
--- a/src/lib/use-outside-click/index.tsx
+++ b/src/lib/use-outside-click/index.tsx
@@ -6,7 +6,35 @@ import { useEventListener } from '../use-event-listener'
/**
* @description
- * A hook that fires the given callback when clicked outside anywhere of the given html element.
+ * A React hook that detects outside click for specified element and triggers the given callback.
+ *
+ * @example
+ import { useRef, useState } from 'react'
+ import { useOutsideClick } from 'classic-react-hooks'
+
+ function Modal() {
+ const [isOpen, setIsOpen] = useState(false)
+ const modalRef = useRef(null)
+
+ useOutsideClick({
+ target: () => modalRef.current,
+ handler: () => setIsOpen(false),
+ })
+
+ if (!isOpen) {
+ return
+ }
+
+ return (
+
+
+
Modal Title
+
Click outside this modal to close it.
+
+
+
+ )
+ }
*
* @see Docs https://classic-react-hooks.vercel.app/hooks/use-outside-click.html
*/
diff --git a/src/lib/use-synced-effect/index.tsx b/src/lib/use-synced-effect/index.tsx
index 0be5030..8801620 100644
--- a/src/lib/use-synced-effect/index.tsx
+++ b/src/lib/use-synced-effect/index.tsx
@@ -6,9 +6,24 @@ const DEP: DependencyList = []
/**
* @description
- * A hooks that fires the given callback for given dependencies.
- *
- * It works exacatly like `useEffect`. But callback doesn't get fired on initial mount.
+ * A React hook that executes a callback when dependencies change, similar to `useEffect`, but skips execution on the initial mount.
+ * @example
+ import { useState } from 'react'
+ import { useSyncedEffect } from 'classic-react-hooks'
+
+ export default function YourComponent() {
+ const [counter, setCounter] = useState(0)
+
+ useSyncedEffect(() => {
+ console.log('counter changed to ', counter)
+ }, [counter])
+
+ return (
+
+
+
+ )
+ }
*
* @see Docs https://classic-react-hooks.vercel.app/hooks/use-synced-effect.html
*/
diff --git a/src/lib/use-synced-ref/index.tsx b/src/lib/use-synced-ref/index.tsx
index 306c8bf..8d0a969 100644
--- a/src/lib/use-synced-ref/index.tsx
+++ b/src/lib/use-synced-ref/index.tsx
@@ -3,9 +3,32 @@ import React, { useRef } from 'react'
/**
* @description
- * A replacement for `useRef` hook, which automatically synces up with the given state.
+ * A React hook that creates a ref that automatically stays in sync with the provided value.
*
- * No need to manually update the ref.
+ * @example
+ import { useState } from 'react'
+ import { useSyncedRef } from 'classic-react-hooks'
+
+ export default function Counter() {
+ const [count, setCount] = useState(0)
+ const countRef = useSyncedRef(count)
+
+ const handleAsyncOperation = () => {
+ setTimeout(() => {
+ // countRef.current always has the latest value
+ console.log('Current count:', countRef.current)
+ alert(`Count is now: ${countRef.current}`)
+ }, 2000)
+ }
+
+ return (
+
+ )
+ }
*
* @see Docs https://classic-react-hooks.vercel.app/hooks/use-throttled-fn.html
*
diff --git a/src/lib/use-timeout-effect/index.tsx b/src/lib/use-timeout-effect/index.tsx
index 5ee525b..fc5e0cf 100644
--- a/src/lib/use-timeout-effect/index.tsx
+++ b/src/lib/use-timeout-effect/index.tsx
@@ -4,8 +4,25 @@ import useSyncedRef from '../use-synced-ref'
/**
* @description
- * A hook which fires the provided callback only once when the given timeout is passed, just like the setTimeout.
+ * A React hook that fires a provided callback after a specified timeout, similar to `setTimeout`, but with additional control methods for clearing and restarting the timer.
*
+ * @example
+ * import { useState } from 'react'
+ import useTimeoutEffect from './useTimeoutEffect'
+
+ export default function BasicExample() {
+ const [message, setMessage] = useState('')
+
+ useTimeoutEffect({
+ handler: () => {
+ setMessage('Timer executed!')
+ },
+ timeout: 2000,
+ })
+
+ return
{message}
+ }
+ *
* @see Docs https://classic-react-hooks.vercel.app/hooks/use-timeout-effect.html
*/
export default function useTimeoutEffect({ handler, timeout = 100 }: { handler: () => void; timeout?: number }) {
diff --git a/src/lib/use-window-resize/index.tsx b/src/lib/use-window-resize/index.tsx
index 62264e0..9820673 100644
--- a/src/lib/use-window-resize/index.tsx
+++ b/src/lib/use-window-resize/index.tsx
@@ -4,8 +4,30 @@ import { useEventListener } from '../use-event-listener'
/**
* @description
- * A hook which evaluates the passed callback on window resize event and returns the result of that callback.
- *
+ * - A React hook that evaluates a callback function on window resize events and returns the result.
+ * - Perfect for responsive behavior based on window dimensions.
+ * @example
+ import { useWindowResize } from 'classic-react-hooks'
+
+ function ResponsiveComponent() {
+ const breakpoint = useWindowResize(() => {
+ const width = window.innerWidth
+ if (width < 640) return 'sm'
+ if (width < 768) return 'md'
+ if (width < 1024) return 'lg'
+ return 'xl'
+ })
+
+ return (
+
+ )
+}
+```
+
+#### Conditional Rendering Based on Connectivity
+
+```ts
+import { useCanReachToInternet } from 'classic-react-hooks'
+
+function DataFetchingComponent() {
+ const { isFullyConnected, isCheckingConnection } = useCanReachToInternet()
+
+ if (isCheckingConnection) {
+ return
Checking connection...
+ }
+
+ if (!isFullyConnected) {
+ return (
+
+
No Internet Connection
+
Please check your connection and try again.
+
+ )
+ }
+
+ return
+}
+```
+
+### Common Use Cases
+
+- User experience: Show connection status, disable features when offline
+- Error handling: Distinguish between network errors and server errors
+- Auto-retry logic: Retry failed requests when connectivity is restored
+
+### Important Notes
+
+- Performance Considerations:
+ - Network polling makes regular HTTP requests - use appropriate intervals
+ - Consider battery usage on mobile devices with frequent polling
+ - The hook automatically cleans up requests to prevent memory leaks
+- CORS Limitations:
+ - Uses `mode: 'no-cors'` for broader compatibility
+ - Some URLs might not work due to CORS policies
+ - Default test URL `(https://dns.google)` is chosen for reliability
diff --git a/apps/doc/hooks/use-is-online.md b/apps/doc/hooks/use-is-online.md
deleted file mode 100644
index d8e2a4b..0000000
--- a/apps/doc/hooks/use-is-online.md
+++ /dev/null
@@ -1,37 +0,0 @@
----
-outline: deep
----
-
-# use-is-online
-
-A React hook that provides real-time network connection status using the browser's `navigator.onLine` API.
-
-### Features
-
-- **Real-time updates:** Real-time network status updates
-- **SSR safe:** SSR-safe with proper hydration handling
-- **Lightweight:** Lightweight with no external dependencies
-- **Core hook:** Built on React's useSyncExternalStore for optimal performance
-
-### Returns
-
-- `isOnline (boolean):` Current network connection state
-
- - `true` when the browser is online
- - `false` when the browser is offline
-
-### Usage Examples
-
-#### Basic Network query
-
-```ts
-import { useIsOnline } from 'classic-react-hooks'
-
-function NetworkStatus() {
- const isOnline = useIsOnline()
-
- return
Connection: {isOnline ? '🟢 Online' : '🔴 Offline'}
-}
-```
-
-### Important Notes
diff --git a/apps/doc/hooks/use-synced-ref.md b/apps/doc/hooks/use-synced-ref.md
index c61089e..4d950e4 100644
--- a/apps/doc/hooks/use-synced-ref.md
+++ b/apps/doc/hooks/use-synced-ref.md
@@ -10,8 +10,8 @@ outline: deep
### Features
- **Reactive:** Automatic synchronization with any value
-- **Prevent State Closure:**Prevents stale closure problems
-- **No Re-render:**Zero re-renders - purely ref-based
+- **Prevent State Closure:** Prevents stale closure problems
+- **No Re-render:** Zero re-renders - purely ref-based
### Parameters
diff --git a/src/index.tsx b/src/index.tsx
index eeea98e..f54fc8d 100644
--- a/src/index.tsx
+++ b/src/index.tsx
@@ -10,7 +10,11 @@ export { default as useLocalStorage } from './lib/use-local-storage'
export { default as useDebouncedFn, debouncedFnWrapper } from './lib/use-debounced-fn'
export { default as useThrottledFn, throttledFnWrapper } from './lib/use-throttled-fn'
-export { default as useIsOnline } from './lib/use-is-online'
+export { default as useCanReachToInternet } from './lib/use-can-reach-to-internet'
+export {
+ useCanReachToInternetCtx,
+ CanReachToInternetCtxProvider as CanReachToInternetProvider,
+} from './lib/use-can-reach-to-internet/can-reach-to-internet-context'
export { default as useSyncedRef } from './lib/use-synced-ref'
diff --git a/src/lib/use-can-reach-to-internet/can-reach-to-internet-context.tsx b/src/lib/use-can-reach-to-internet/can-reach-to-internet-context.tsx
new file mode 100644
index 0000000..f43d223
--- /dev/null
+++ b/src/lib/use-can-reach-to-internet/can-reach-to-internet-context.tsx
@@ -0,0 +1,103 @@
+'use client'
+import type { ReactNode } from 'react'
+
+import { createContext, useContext } from 'react'
+import useCanReachToInternet, { CanReachToInternetOptions } from '.'
+
+/**
+ * Type definition for the context values containing all return values from useCanReachToInternet hook
+ */
+type CtxValues = ReturnType
+
+/**
+ * React Context for sharing internet connectivity status across the component tree
+ *
+ * @description
+ * This context provides all the connectivity status and control methods from
+ * useCanReachToInternet hook to any component in the React component tree
+ * without prop drilling.
+ *
+ */
+const CanReachToInternetCtx = createContext({} as CtxValues)
+
+/**
+ * Props interface for the CanReachToInternetCtxProvider component
+ */
+interface CanReachToInternetCtxProviderProps extends CanReachToInternetOptions {
+ /** React children components that will have access to the connectivity context */
+ children: ReactNode
+}
+
+/**
+ * Context Provider component for internet connectivity monitoring
+ *
+ * @description
+ * This provider component wraps your application or component tree to provide
+ * internet connectivity status and controls to all child components. It uses
+ * the useCanReachToInternet hook internally and shares its values through React Context.
+ *
+ * The provider accepts all the same configuration options as the useCanReachToInternet
+ * hook, allowing you to configure network polling, test URLs, and polling intervals
+ * at the application level.
+ *
+ *
+ * @example
+ * // Basic usage - wrap your app with the provider
+ import { CanReachToInternetCtxProvider } from 'classic-react-hooks'
+
+ function App() {
+ return (
+
+
+
+
+
+ )
+ }
+ *
+ * */
+export function CanReachToInternetCtxProvider({ children, ...options }: CanReachToInternetCtxProviderProps) {
+ const values = useCanReachToInternet(options)
+
+ return {children}
+}
+
+/**
+ * Custom hook to consume the internet connectivity context
+ *
+ * @description
+ * This hook provides access to all the internet connectivity status and control
+ * methods from the nearest CanReachToInternetCtxProvider in the component tree.
+ * It must be used within a component that is wrapped by CanReachToInternetCtxProvider,
+ * otherwise it will throw an error.
+ *
+ * The hook returns the same values as useCanReachToInternet, but accessed through
+ * React Context instead of being created locally in each component.
+ *
+ * @throws {Error} Throws an error if used outside of CanReachToInternetCtxProvider
+ *
+ * @example
+ * // Basic usage in a component
+ import { useCanReachToInternetCtx } from 'classic-react-hooks'
+
+ function NetworkStatusBadge() {
+ const { isFullyConnected, isCheckingConnection } = useCanReachToInternetCtx()
+
+ if (isCheckingConnection) {
+ return
Checking...
+ }
+
+ return (
+
+ {isFullyConnected ? '🟢 Online' : '🔴 Offline'}
+
+ )
+ }
+ *
+ * */
+export function useCanReachToInternetCtx() {
+ if (!CanReachToInternetCtx) {
+ throw new Error('useCanReachToInternetCtx must be used within an CanReachToInternetCtxProvider')
+ }
+ return useContext(CanReachToInternetCtx)
+}
diff --git a/src/lib/use-can-reach-to-internet/index.tsx b/src/lib/use-can-reach-to-internet/index.tsx
new file mode 100644
index 0000000..0189f24
--- /dev/null
+++ b/src/lib/use-can-reach-to-internet/index.tsx
@@ -0,0 +1,215 @@
+'use client'
+
+import { useEffect, useRef, useState, useSyncExternalStore } from 'react'
+import useSyncedRef from '../use-synced-ref'
+
+export type CanReachToInternetOptions = {
+ /** Enable automatic network polling to continuously check connectivity */
+ enableNetworkPolling?: boolean
+ /** Interval in milliseconds between network polls */
+ networkPollingInterval?: number
+ /** URL to test internet connectivity against */
+ testUrl?: string
+}
+export type CanReachToInternetBoolean = boolean
+
+const DEFAULT_OPTIONS: Required = {
+ enableNetworkPolling: true,
+ networkPollingInterval: 3000,
+ testUrl: 'https://dns.google', // https://8.8.8.8
+}
+
+/**
+ * Custom React hook for monitoring comprehensive internet connectivity status
+ *
+ * @description
+ * This hook provides a robust solution for detecting internet connectivity by combining:
+ * - Browser's native online/offline detection (navigator.onLine)
+ * - Actual network reachability testing via HTTP requests
+ * - Automatic polling with configurable intervals
+ * - Manual connectivity checking capabilities
+ *
+ * The hook differentiates between being "online" (browser thinks it's connected)
+ * and actually being able to reach the internet (verified through network requests).
+ * @example
+ * import { useCanReachToInternet } from 'classic-react-hooks'
+ *
+ * function ConnectivityStatus() {
+ const {
+ isOnline,
+ canReachToInternet,
+ isFullyConnected,
+ isCheckingConnection,
+ isNetworkPollingEnabled,
+ startNetworkPolling,
+ stopNetworkPolling,
+ forceCheckNetwork
+ } = useCanReachToInternet()
+
+ return (
+
+
Connectivity Status
+
+
+
+
+ Browser Online: {isOnline ? 'Yes' : 'No'}
+
+
+
+
+ Internet Reachable: {canReachToInternet ? 'Yes' : 'No'}
+
+ )
+ }
+ *
+ * @see Docs https://classic-react-hooks.vercel.app/hooks/use-can-reach-to-internet.html
+ */
+export default function useCanReachToInternet(options: CanReachToInternetOptions = {}) {
+ const config = { ...DEFAULT_OPTIONS, ...options }
+
+ // Getting browser's online/offline status
+ const isOnline = useSyncExternalStore(subscribe, getSnapshot, () => true)
+
+ const [canReachToInternet, setCanReachToInternet] = useState(false)
+ const [isNetworkPollingEnabled, setIsNetworkPollingEnabled] = useState(config.enableNetworkPolling)
+ const [isCheckingConnection, setIsCheckingConnection] = useState(false)
+ const canReachToInternetRef = useSyncedRef(canReachToInternet)
+
+ // Use refs to track cleanup and prevent memory leaks
+ const abortControllerRef = useRef(null)
+ const timeoutRef = useRef(null)
+
+ const handlers = useRef({
+ clearPendingOperations: () => {
+ if (abortControllerRef.current) {
+ abortControllerRef.current.abort('cleanup')
+ abortControllerRef.current = null
+ }
+ if (timeoutRef.current) {
+ clearTimeout(timeoutRef.current)
+ timeoutRef.current = null
+ }
+ },
+ stopNetworkPolling: () => setIsNetworkPollingEnabled(false),
+ startNetworkPolling: () => setIsNetworkPollingEnabled(true),
+ getCanReachToInternetStatus: () => canReachToInternetRef.current as CanReachToInternetBoolean,
+ })
+
+ /**
+ * Performs an actual network request to test internet connectivity
+ *
+ * @description
+ * This function makes a HEAD request to the configured test URL to verify
+ * that the device can actually reach the internet, not just that the browser
+ * thinks it's online.
+ *
+ */
+ const checkIfCanReachToInternet = async () => {
+ // If offline then early return with flag update
+ if (!isOnline) {
+ setCanReachToInternet(false)
+ return
+ }
+
+ // Clear any existing operations
+ handlers.current.clearPendingOperations()
+
+ const abortController = new AbortController()
+ abortControllerRef.current = abortController
+
+ setIsCheckingConnection(true)
+
+ try {
+ // ping to dns server to check if really connected to internet
+ await fetch(config.testUrl, {
+ method: 'HEAD',
+ cache: 'no-cache',
+ mode: 'no-cors',
+ headers: {
+ 'Cache-Control': 'no-cache, no-store, must-revalidate',
+ Pragma: 'no-cache',
+ },
+ signal: abortController.signal,
+ })
+ setCanReachToInternet(true)
+ } catch (err) {
+ // Only update state if the request wasn't aborted
+ if (!abortController.signal.aborted) {
+ setCanReachToInternet(false)
+ }
+ } finally {
+ if (isNetworkPollingEnabled && !abortController.signal.aborted) {
+ timeoutRef.current = setTimeout(() => checkIfCanReachToInternet(), config.networkPollingInterval)
+ }
+ setIsCheckingConnection(false)
+ }
+ }
+
+ useEffect(() => {
+ checkIfCanReachToInternet()
+
+ return handlers.current.clearPendingOperations
+ }, [isOnline, isNetworkPollingEnabled, config.networkPollingInterval])
+
+ return {
+ isOnline,
+ canReachToInternet,
+ isFullyConnected: isOnline && canReachToInternet,
+ isNetworkPollingEnabled,
+ isCheckingConnection,
+ stopNetworkPolling: handlers.current.stopNetworkPolling,
+ startNetworkPolling: handlers.current.startNetworkPolling,
+ getCanReachToInternetStatus: handlers.current.getCanReachToInternetStatus,
+ forceCheckNetwork: checkIfCanReachToInternet,
+ }
+}
+
+// Handler to listen "online" and "offline" events
+function subscribe(callback: () => void) {
+ window.addEventListener('online', callback)
+ window.addEventListener('offline', callback)
+
+ return () => {
+ window.addEventListener('online', callback)
+ window.addEventListener('offline', callback)
+ }
+}
+// getting network connection status
+const getSnapshot = () => navigator.onLine
diff --git a/src/lib/use-is-online/index.tsx b/src/lib/use-is-online/index.tsx
deleted file mode 100644
index 5b5e30b..0000000
--- a/src/lib/use-is-online/index.tsx
+++ /dev/null
@@ -1,109 +0,0 @@
-'use client'
-import React, { createContext, ReactNode, useContext, useEffect, useRef, useState, useSyncExternalStore } from 'react'
-
-type CanReachToInternetBoolean = boolean
-type CanReachToInternetOptions = {
- enableNetworkPolling?: boolean
- networkPollingInterval?: number
-}
-
-/**
- * @description
- * A simple hook for getting the network connection state.
- *
- * @see Docs https://classic-react-hooks.vercel.app/hooks/use-is-online.html
- */
-export default function useCanReachToInternet(
- options: CanReachToInternetOptions = {
- enableNetworkPolling: true,
- networkPollingInterval: 5000,
- }
-) {
- const [canReachToInternet, setCanReachToInternet] = useState(false)
- const isOnline = useSyncExternalStore(subscribe, getSnapshot, () => true)
-
- const states = useRef<{
- abortControllerRef: AbortController | undefined
- timeoutId: NodeJS.Timeout | undefined
- checkIfCanReachToInternet: () => Promise
- stopNetworkPolling: () => void
- restartNetworkPolling: () => void
- }>({
- abortControllerRef: undefined,
- timeoutId: undefined,
- stopNetworkPolling: () => {
- clearTimeout(states.current.timeoutId)
- states.current.abortControllerRef?.abort()
- },
- restartNetworkPolling: () => {
- states.current.checkIfCanReachToInternet()
- },
- checkIfCanReachToInternet: async () => {
- try {
- // ping to Google dns server to check if really connected to internet
- // https://8.8.8.8 google-dns
- await fetch('https://dns.google', {
- method: 'HEAD',
- cache: 'no-cache',
- mode: 'no-cors',
- headers: {
- 'Cache-Control': 'no-cache, no-store, must-revalidate',
- Pragma: 'no-cache',
- },
- signal: states.current.abortControllerRef?.signal,
- })
- setCanReachToInternet(true)
- } catch (err) {
- setCanReachToInternet(false)
- } finally {
- if (options.enableNetworkPolling) {
- setTimeout(() => states.current.checkIfCanReachToInternet(), options.networkPollingInterval)
- }
- }
- },
- })
- useEffect(() => {
- if (!isOnline) {
- setCanReachToInternet(false)
- } else {
- states.current.checkIfCanReachToInternet()
- }
-
- return () => {
- clearTimeout(states.current.timeoutId)
- states.current.abortControllerRef?.abort()
- }
- }, [isOnline])
-
- return {
- isOnline: canReachToInternet,
- stopNetworkPolling: states.current.stopNetworkPolling,
- restartNetworkPolling: states.current.restartNetworkPolling,
- }
-}
-
-function subscribe(callback: () => void) {
- window.addEventListener('online', callback)
- window.addEventListener('offline', callback)
-
- return () => {
- window.addEventListener('online', callback)
- window.addEventListener('offline', callback)
- }
-}
-const getSnapshot = () => navigator.onLine
-
-type CtxValues = ReturnType
-
-const CanReachToCtx = createContext({} as CtxValues)
-
-function useIsOnlineValue() {
- const { isOnline } = useContext(CanReachToCtx)
- return isOnline
-}
-
-function CanReachToInternetProvider(props: CanReachToInternetOptions & { children: ReactNode }) {
- const values = useCanReachToInternet(props)
-
- return {props.children}
-}
From 5509803e0dfb06feddf4338b19423d9a0d759d5d Mon Sep 17 00:00:00 2001
From: "github-actions[bot]"
<41898282+github-actions[bot]@users.noreply.github.com>
Date: Fri, 27 Jun 2025 16:58:49 +0000
Subject: [PATCH 19/81] Version Packages (canary)
---
.changeset/pre.json | 1 +
CHANGELOG.md | 8 ++++++++
package.json | 2 +-
3 files changed, 10 insertions(+), 1 deletion(-)
diff --git a/.changeset/pre.json b/.changeset/pre.json
index 5d77741..6e5ff94 100644
--- a/.changeset/pre.json
+++ b/.changeset/pre.json
@@ -6,6 +6,7 @@
},
"changesets": [
"icy-boats-make",
+ "pink-pugs-eat",
"tender-sites-occur"
]
}
diff --git a/CHANGELOG.md b/CHANGELOG.md
index ec7dbf0..de4dae5 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,13 @@
# classic-react-hooks
+## 2.0.0-canary.2
+
+### Minor Changes
+
+- 703c876: - breaking: remove useIsOnline hook
+ - Feat: add useCanReachToInternet for getting network connection
+ - Feat: add useCanReachToInternetCtx and CanReachToInternetCtxProvider for subscribing the application to get network reachability with context
+
## 2.0.0-canary.1
### Minor Changes
diff --git a/package.json b/package.json
index 5a8db9c..84cc844 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "classic-react-hooks",
- "version": "2.0.0-canary.1",
+ "version": "2.0.0-canary.2",
"description": "A great collection of React utility hooks",
"keywords": [
"classic-react-hooks",
From 3e110b23542b8800d2b7311a7acd9abdc8bcfac2 Mon Sep 17 00:00:00 2001
From: Ashish-simpleCoder
Date: Tue, 1 Jul 2025 21:07:19 +0530
Subject: [PATCH 20/81] Docs: add new sections and update docs
- Doc: Add doc for useCanReachToInternet
- Doc: Refactor the docs for hooks
---
.changeset/cuddly-melons-turn.md | 10 ++++++++
apps/doc/hooks/use-can-reach-to-internet.md | 28 ++++++++++++++++++---
apps/doc/hooks/use-copy-to-clipboard.md | 18 ++++++-------
apps/doc/hooks/use-counter.md | 5 ++--
apps/doc/hooks/use-debounced-fn.md | 9 ++++---
apps/doc/hooks/use-intersection-observer.md | 2 +-
apps/doc/hooks/use-local-storage.md | 7 +++---
apps/doc/hooks/use-on-mount-effect.md | 4 +--
apps/doc/hooks/use-outside-click.md | 5 ++--
apps/doc/hooks/use-synced-effect.md | 6 ++---
apps/doc/hooks/use-synced-ref.md | 5 ++--
apps/doc/hooks/use-throttled-fn.md | 5 ++--
apps/doc/hooks/use-window-resize.md | 13 +++++-----
13 files changed, 76 insertions(+), 41 deletions(-)
create mode 100644 .changeset/cuddly-melons-turn.md
diff --git a/.changeset/cuddly-melons-turn.md b/.changeset/cuddly-melons-turn.md
new file mode 100644
index 0000000..fbb3420
--- /dev/null
+++ b/.changeset/cuddly-melons-turn.md
@@ -0,0 +1,10 @@
+---
+'classic-react-hooks': minor
+---
+
+
+- Doc: Add `what problem it solves` doc for useCanReachToInternet
+- Doc: Refactor the docs for hooks
+
+
+
diff --git a/apps/doc/hooks/use-can-reach-to-internet.md b/apps/doc/hooks/use-can-reach-to-internet.md
index 8300fe2..99ada18 100644
--- a/apps/doc/hooks/use-can-reach-to-internet.md
+++ b/apps/doc/hooks/use-can-reach-to-internet.md
@@ -20,9 +20,7 @@ A comprehensive React hook for monitoring internet connectivity status that goes
| --------- | :---------------------------------: | :------: | :-----------: | -------------------------------------------------- |
| options | [CanReachToInternetOptions](#types) | ❌ | {} | Configuration object for customizing hook behavior |
-#### CanReachToInternetOptions
-
-#### Types
+#### Parameter Types
```ts
type CanReachToInternetOptions = {
@@ -96,6 +94,30 @@ function DataFetchingComponent() {
}
```
+### Problem It Solves
+
+---
+
+#### The Problem with `navigator.onLine`
+
+`navigator.onLine` only tells you if the browser thinks it's connected to a network, not if it can actually reach the internet.
+
+##### Common Scenarios Where `navigator.onLine` Fails
+
+- **Limited Connectivity:** Your device is connected to a router, but the router has no internet connection. The browser sees the local network connection and reports online status as true.
+- **Network Issues:** DNS problems or ISP outages where you have network connection but can't reach to external servers.
+- **Captive Portals:** You're connected to WiFi at a hotel, airport but haven't authenticated yet. `navigator.onLine` returns true, but you can't access any websites.
+
+---
+
+#### How `useCanReachToInternet` solve these problems
+
+It provides two layers of connectivity detection
+
+- **`isOnline`:** Browser's basic network status (via `navigator.onLine`)
+- **`canReachToInternet`:** Actual internet reachability (via real HTTP requests to a test server)
+- **`isFullyConnected`:** Both conditions must be true for genuine internet access
+
### Common Use Cases
- User experience: Show connection status, disable features when offline
diff --git a/apps/doc/hooks/use-copy-to-clipboard.md b/apps/doc/hooks/use-copy-to-clipboard.md
index 1945c40..6bbbba8 100644
--- a/apps/doc/hooks/use-copy-to-clipboard.md
+++ b/apps/doc/hooks/use-copy-to-clipboard.md
@@ -8,17 +8,9 @@ outline: deep
### Parameters
-| Parameter | Type | Required | Default Value | Description |
-| --------- | :---------------------: | :------: | :-----------: | ----------- |
-| Object | [Props](#parametertype) | ❌ | - | Object |
-
-### Returns
-
-[`CopyToClipboardFn`](#returntype) : A function for copying the data into clipboard
-
-### Types
-
----
+| Parameter | Type | Required | Default Value | Description |
+| --------- | :---------------------: | :------: | :-----------: | -------------------------------- |
+| Props | [Props](#parametertype) | ❌ | - | Pass success and error callbacks |
#### ParameterType
@@ -32,6 +24,10 @@ type OnSuccess = () => void
type OnError = (err: Error) => void
```
+### Returns
+
+[`CopyToClipboardFn`](#returntype) - A function for copying the data into clipboard
+
#### ReturnType
```ts
diff --git a/apps/doc/hooks/use-counter.md b/apps/doc/hooks/use-counter.md
index ac617a0..259c60f 100644
--- a/apps/doc/hooks/use-counter.md
+++ b/apps/doc/hooks/use-counter.md
@@ -4,8 +4,9 @@ outline: deep
# use-counter
-- A Hook for Fun
-- A type-safe React hook for managing counter state with customizable step values and dynamic property naming.
+A Hook for Fun
+
+A type-safe React hook for managing counter state with customizable step values and dynamic property naming.
#### Features
diff --git a/apps/doc/hooks/use-debounced-fn.md b/apps/doc/hooks/use-debounced-fn.md
index d2e1042..7046ea2 100644
--- a/apps/doc/hooks/use-debounced-fn.md
+++ b/apps/doc/hooks/use-debounced-fn.md
@@ -4,13 +4,14 @@ outline: deep
# use-debouced-fn
-- A React hook that returns a debounced version of any function, delaying its execution until after a specified delay has passed since the last time it was invoked.
-- Perfect for optimizing performance in scenarios like search inputs, API calls, or resize handlers.
+A React hook that returns a debounced version of any function, delaying its execution until after a specified delay has passed since the last time it was invoked.
+
+Perfect for optimizing performance in scenarios like search inputs, API calls, or resize handlers.
### Features
-- **Debouncing Functionality:** The primary feature is delaying function execution until after a specified period of inactivity. If the function is called again before the delay expires, the previous call is cancelled and the timer resets.
-- **Configurable Delay:** You can specify a custom delay period, with a sensible default of 300ms if none is provided.
+- **Debouncing Functionality:** Delaying function execution until after a specified period of inactivity. Calling the function again before the delay expires, the previous call is cancelled and the timer resets.
+- **Configurable Delay:** You can specify a custom delay period, with a sensible default of 300ms.
- **Dynamic Props Updates:** The hook properly handles updates to both the callback function and delay value during re-renders without losing the debouncing behavior.
- **Performance optimized:** Prevents excessive function calls
- **Auto cleanup:** Automatically clears timers on component unmount and on delay prop change
diff --git a/apps/doc/hooks/use-intersection-observer.md b/apps/doc/hooks/use-intersection-observer.md
index de0e1e9..7adf4a9 100644
--- a/apps/doc/hooks/use-intersection-observer.md
+++ b/apps/doc/hooks/use-intersection-observer.md
@@ -43,7 +43,7 @@ export interface IntersectionOptions extends IntersectionObserverInit {
}
```
-### Return Value
+### Returns
Returns an array of boolean values (`Array`) where each boolean represents whether the corresponding target element is currently intersecting (visible) or not.
diff --git a/apps/doc/hooks/use-local-storage.md b/apps/doc/hooks/use-local-storage.md
index 0def9b3..43559ca 100644
--- a/apps/doc/hooks/use-local-storage.md
+++ b/apps/doc/hooks/use-local-storage.md
@@ -4,15 +4,16 @@ outline: deep
# use-local-storage
-- A React hook that synchronizes state with localStorage, providing `persistent` state management across browser `sessions`.
-- Works exactly like `useState` but automatically persists data to localStorage.
+A React hook that synchronizes state with localStorage, providing `persistent` state management across browser `sessions`.
+
+Works exactly like `useState` but automatically persists data to localStorage.
### Features
- **Automatic sync:** Automatic localStorage synchronization
- **Persistence updates:** Seamless state updates with persistence
- **Error handling:** Built-in error handling and fallbacks
-- **Compatible API:** useState-compatible API
+- **Compatible API:** `useState` compatible API
- **Automatic parsing:** JSON serialization/deserialization
### Parameters
diff --git a/apps/doc/hooks/use-on-mount-effect.md b/apps/doc/hooks/use-on-mount-effect.md
index 9b879f0..242351c 100644
--- a/apps/doc/hooks/use-on-mount-effect.md
+++ b/apps/doc/hooks/use-on-mount-effect.md
@@ -4,9 +4,9 @@ outline: deep
# use-on-mount-effect
-- A React hook that executes a callback function only once after the component mounts. This is a simplified wrapper around useEffect with an empty dependency array.
+A React hook that executes a callback function only once after the component mounts. This is a simplified wrapper around useEffect with an empty dependency array.
-- This hook is perfect for initialization logic that should run exactly once when a component first renders.
+This hook is perfect for initialization logic that should run exactly once when a component first renders.
### Parameters
diff --git a/apps/doc/hooks/use-outside-click.md b/apps/doc/hooks/use-outside-click.md
index d12ddae..3be40ca 100644
--- a/apps/doc/hooks/use-outside-click.md
+++ b/apps/doc/hooks/use-outside-click.md
@@ -4,8 +4,9 @@ outline: deep
# use-outside-click
-- A React hook that detects outside click for specified element and triggers the given callback.
-- Perfect for implementing modals, dropdowns and other UI components that need to be closed when users click outside of them.
+A React hook that detects outside click for specified element and triggers the given callback.
+
+Perfect for implementing modals, dropdowns and other UI components that need to be closed when users click outside of them.
### Features
diff --git a/apps/doc/hooks/use-synced-effect.md b/apps/doc/hooks/use-synced-effect.md
index 11d85d0..545032f 100644
--- a/apps/doc/hooks/use-synced-effect.md
+++ b/apps/doc/hooks/use-synced-effect.md
@@ -4,8 +4,9 @@ outline: deep
# use-synced-effect
-- A React hook that executes a callback when dependencies change, similar to `useEffect`, but skips execution on the initial mount.
-- This is particularly useful when you want to respond to state changes without triggering side effects during the component's first render.
+A React hook that executes a callback when dependencies change, similar to `useEffect`, but skips execution on the initial mount.
+
+This is particularly useful when you want to respond to state changes without triggering side effects during the component's first render.
### Features
@@ -89,7 +90,6 @@ function SearchComponent() {
### Important notes
- Empty dependency array [] means the effect will never run (since there are no dependencies to change)
-- No dependency array means the effect will never run (same as array [])
### Common Use Cases
diff --git a/apps/doc/hooks/use-synced-ref.md b/apps/doc/hooks/use-synced-ref.md
index 4d950e4..949d3e4 100644
--- a/apps/doc/hooks/use-synced-ref.md
+++ b/apps/doc/hooks/use-synced-ref.md
@@ -4,8 +4,9 @@ outline: deep
# use-synced-ref
-- A React hook that creates a ref that automatically stays in sync with the provided value.
-- This eliminates the need to manually update refs and helps avoid stale closure issues in callbacks and effects.
+A React hook that creates a ref that automatically stays in sync with the provided value.
+
+This eliminates the need to manually update refs and helps avoid stale closure issues in callbacks and effects.
### Features
diff --git a/apps/doc/hooks/use-throttled-fn.md b/apps/doc/hooks/use-throttled-fn.md
index 8e792bd..e944dad 100644
--- a/apps/doc/hooks/use-throttled-fn.md
+++ b/apps/doc/hooks/use-throttled-fn.md
@@ -6,8 +6,9 @@ outline: deep
A React hook that returns a throttled version of a callback function.
-- Throttling ensures that the function is called at most once per specified time interval, regardless of how many times it's invoked.
-- This is particularly useful for performance optimization in scenarios like handling rapid user input, scroll events, or API calls.
+Throttling ensures that the function is called at most once per specified time interval, regardless of how many times it's invoked.
+
+This is particularly useful for performance optimization in scenarios like handling rapid user input, scroll events, or API calls.
### Features
diff --git a/apps/doc/hooks/use-window-resize.md b/apps/doc/hooks/use-window-resize.md
index 68be94c..ce0224f 100644
--- a/apps/doc/hooks/use-window-resize.md
+++ b/apps/doc/hooks/use-window-resize.md
@@ -4,15 +4,16 @@ outline: deep
# use-window-resize
-- A React hook that evaluates a callback function on window resize events and returns the result.
-- Perfect for responsive behavior based on window dimensions.
+A React hook that evaluates a callback function on window resize events and returns the result.
+
+Perfect for responsive behavior based on window dimensions.
### Features
-- **Custom handler** Execute custom logic on window resize
-- **Reactive** Automatic re-evaluation and state updates
-- **Configurable** Configurable default values and event injection
-- **Underlying hook** At its core, it uses `useEventListener` hook
+- **Custom handler:** Execute custom logic on window resize
+- **Reactive:** Automatic re-evaluation and state updates
+- **Configurable:** Configurable default values and event injection
+- **Underlying hook:** At its core, it uses `useEventListener` hook
### Parameters
From 8d4267f2969cb2133cdbf7993c2b25bfeec18783 Mon Sep 17 00:00:00 2001
From: "github-actions[bot]"
<41898282+github-actions[bot]@users.noreply.github.com>
Date: Thu, 3 Jul 2025 14:59:10 +0000
Subject: [PATCH 21/81] Version Packages (canary)
---
.changeset/pre.json | 1 +
CHANGELOG.md | 7 +++++++
package.json | 2 +-
3 files changed, 9 insertions(+), 1 deletion(-)
diff --git a/.changeset/pre.json b/.changeset/pre.json
index 6e5ff94..30cc400 100644
--- a/.changeset/pre.json
+++ b/.changeset/pre.json
@@ -5,6 +5,7 @@
"classic-react-hooks": "1.4.0"
},
"changesets": [
+ "cuddly-melons-turn",
"icy-boats-make",
"pink-pugs-eat",
"tender-sites-occur"
diff --git a/CHANGELOG.md b/CHANGELOG.md
index de4dae5..e3eb8dc 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,12 @@
# classic-react-hooks
+## 2.0.0-canary.3
+
+### Minor Changes
+
+- 3e110b2: - Doc: Add `what problem it solves` doc for useCanReachToInternet
+ - Doc: Refactor the docs for hooks
+
## 2.0.0-canary.2
### Minor Changes
diff --git a/package.json b/package.json
index 84cc844..6e2c874 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "classic-react-hooks",
- "version": "2.0.0-canary.2",
+ "version": "2.0.0-canary.3",
"description": "A great collection of React utility hooks",
"keywords": [
"classic-react-hooks",
From c52bb9f2800e76be5464e585bf78e5cf2c9e3757 Mon Sep 17 00:00:00 2001
From: Ashish-simpleCoder
Date: Thu, 3 Jul 2025 20:46:46 +0530
Subject: [PATCH 22/81] docs: add CanReachToInterernetCtxProvder doc
---
.changeset/good-vans-impress.md | 5 +
apps/doc/.vitepress/config.mts | 12 +-
.../CanReachToInternetCtxProvider.md | 171 ++++++++++++++++++
3 files changed, 187 insertions(+), 1 deletion(-)
create mode 100644 .changeset/good-vans-impress.md
create mode 100644 apps/doc/components/CanReachToInternetCtxProvider.md
diff --git a/.changeset/good-vans-impress.md b/.changeset/good-vans-impress.md
new file mode 100644
index 0000000..f77dbf2
--- /dev/null
+++ b/.changeset/good-vans-impress.md
@@ -0,0 +1,5 @@
+---
+'classic-react-hooks': minor
+---
+
+Add CanReachToInterernetCtxProvder and useCanReachToInternetCtx component's documentation
diff --git a/apps/doc/.vitepress/config.mts b/apps/doc/.vitepress/config.mts
index 0d5b267..36ea154 100644
--- a/apps/doc/.vitepress/config.mts
+++ b/apps/doc/.vitepress/config.mts
@@ -113,7 +113,9 @@ function sidebarGuide(): DefaultTheme.SidebarItem[] {
{ text: 'use-outside-click', link: 'use-outside-click' },
{ text: 'use-debounced-fn', link: 'use-debounced-fn' },
{ text: 'use-throttled-fn', link: 'use-throttled-fn' },
- { text: 'use-can-reach-to-internet', link: 'use-can-reach-to-internet' },
+ { text: 'use-can-reach-to-internet', link: 'use-can-reach-to-internet' ,collapsed: true,items:[
+ { text: 'CanReachToInternetCtxProvider', link: 'components/CanReachToInternetCtxProvider',base:'/' },
+ ]},
{ text: 'use-timeout-effect', link: 'use-timeout-effect' },
{ text: 'use-interval-effect', link: 'use-interval-effect' },
{ text: 'use-synced-ref', link: 'use-synced-ref' },
@@ -122,5 +124,13 @@ function sidebarGuide(): DefaultTheme.SidebarItem[] {
{ text: 'use-counter', link: 'use-counter' },
]
},
+ {
+ text: 'Components',
+ collapsed: false,
+ base: "/components/",
+ items: [
+ { text: 'CanReachToInternetCtxProvider', link: 'CanReachToInternetCtxProvider' },
+ ]
+ },
]
}
diff --git a/apps/doc/components/CanReachToInternetCtxProvider.md b/apps/doc/components/CanReachToInternetCtxProvider.md
new file mode 100644
index 0000000..a0cf50a
--- /dev/null
+++ b/apps/doc/components/CanReachToInternetCtxProvider.md
@@ -0,0 +1,171 @@
+---
+outline: deep
+---
+
+# Internet Connectivity Context
+
+A React Context provider and hook for sharing internet connectivity status across your component tree without prop drilling. Built on top of the `useCanReachToInternet` hook to provide centralized connectivity monitoring.
+
+### Features
+
+- **Centralized connectivity state:** Share connectivity status across your entire app
+- **No prop drilling:** Access connectivity data from any component in the tree
+- **Same configuration options:** All `useCanReachToInternet` options available at provider level
+
+## Components & Hooks
+
+### CanReachToInternetCtxProvider
+
+A context provider component that wraps your application to provide connectivity status to all child components.
+
+#### Props
+
+| Parameter | Type | Required | Default Value | Description |
+| ---------------------- | :---------: | :------: | :--------------------: | --------------------------------------------------------------------------- |
+| children | `ReactNode` | ✅ | - | React children components that will have access to the connectivity context |
+| enableNetworkPolling | `boolean` | ❌ | `true` | Enable automatic network polling to continuously check connectivity |
+| networkPollingInterval | `number` | ❌ | `3000` | Interval in milliseconds between network polls |
+| testUrl | `string` | ❌ | `'https://dns.google'` | URL to test internet connectivity against |
+
+### useCanReachToInternetCtx
+
+A custom hook to consume the internet connectivity context values.
+
+#### Returns
+
+| Property | Type | Description |
+| ----------------------------- | --------------- | -------------------------------------------------------------------------------- |
+| `isOnline` | `boolean` | Browser's native online/offline status from `navigator.onLine` |
+| `canReachToInternet` | `boolean` | Whether the device can actually reach the internet (verified via HTTP request) |
+| `isFullyConnected` | `boolean` | Combined status: `true` when both `isOnline` and `canReachToInternet` are `true` |
+| `isNetworkPollingEnabled` | `boolean` | Current state of automatic network polling |
+| `isCheckingConnection` | `boolean` | Whether a connectivity check is currently in progress |
+| `startNetworkPolling` | `() => void` | Function to start automatic network polling |
+| `stopNetworkPolling` | `() => void` | Function to stop automatic network polling |
+| `forceCheckNetwork` | `() => void` | Function to manually trigger a connectivity check |
+| `getCanReachToInternetStatus` | `() => boolean` | Function to get current internet reachability status |
+
+## Usage Examples
+
+### Basic App Setup
+
+```tsx
+import { CanReachToInternetCtxProvider } from 'classic-react-hooks'
+
+function App() {
+ return (
+
+
+
+
+
+ )
+}
+```
+
+### Custom Configuration
+
+```tsx
+import { CanReachToInternetCtxProvider } from 'classic-react-hooks'
+
+function App() {
+ return (
+
+
+
+
+
+ )
+}
+```
+
+### Network Status Badge Component
+
+```tsx
+import { useCanReachToInternetCtx } from 'classic-react-hooks'
+
+function NetworkStatusBadge() {
+ const { isFullyConnected, isCheckingConnection } = useCanReachToInternetCtx()
+
+ if (isCheckingConnection) {
+ return
Checking...
+ }
+
+ return (
+
+ {isFullyConnected ? '🟢 Online' : '🔴 Offline'}
+
+ )
+}
+```
+
+## Why Use Context Instead of Hook Directly?
+
+### Benefits of Context Approach
+
+- **Single source of truth:** One connectivity state shared across the entire app
+- **Performance:** Only one network polling instance running instead of multiple
+- **Consistency:** All components see the same connectivity status simultaneously
+- **Centralized configuration:** Set polling intervals and test URLs once at the app level
+- **Reduced complexity:** No need to pass connectivity props down through component trees
+
+### When to Use Each Approach
+
+**Use Context when:**
+
+- Multiple components need connectivity status
+- You want centralized connectivity configuration
+- Building a larger application with complex component hierarchy
+- Need consistent connectivity state across the app
+
+**Use Hook directly when:**
+
+- Only one or few components need connectivity status
+- Building simple components or libraries
+- Need different connectivity configurations for different parts of your app
+- Working with isolated features
+
+## Important Notes
+
+### Error Handling
+
+The `useCanReachToInternetCtx` hook will throw an error if used outside of `CanReachToInternetCtxProvider`:
+
+```tsx
+// ❌ This will throw an error
+function ComponentOutsideProvider() {
+ const { isOnline } = useCanReachToInternetCtx() // Error!
+ return
+}
+```
+
+### Performance Considerations
+
+- **Single polling instance:** Context ensures only one network polling operation runs, regardless of how many components consume the context
+- **Memory efficiency:** Automatic cleanup of network requests and timers when provider unmounts
+- **Battery optimization:** Configure appropriate polling intervals for mobile devices
+
+### Best Practices
+
+- Place the provider as high as possible in your component tree for maximum availability
+- Use appropriate polling intervals based on your app's needs (longer intervals for battery-sensitive apps)
+- Consider conditional rendering for offline scenarios to improve user experience
+- Implement proper loading states using `isCheckingConnection` for better UX
From 4f40304b1eac018925f8bca7ec59d1d30f7191f6 Mon Sep 17 00:00:00 2001
From: "github-actions[bot]"
<41898282+github-actions[bot]@users.noreply.github.com>
Date: Thu, 3 Jul 2025 15:22:57 +0000
Subject: [PATCH 23/81] Version Packages (canary)
---
.changeset/pre.json | 1 +
CHANGELOG.md | 6 ++++++
package.json | 2 +-
3 files changed, 8 insertions(+), 1 deletion(-)
diff --git a/.changeset/pre.json b/.changeset/pre.json
index 30cc400..99a70b7 100644
--- a/.changeset/pre.json
+++ b/.changeset/pre.json
@@ -6,6 +6,7 @@
},
"changesets": [
"cuddly-melons-turn",
+ "good-vans-impress",
"icy-boats-make",
"pink-pugs-eat",
"tender-sites-occur"
diff --git a/CHANGELOG.md b/CHANGELOG.md
index e3eb8dc..586c4f2 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,11 @@
# classic-react-hooks
+## 2.0.0-canary.4
+
+### Minor Changes
+
+- c52bb9f: Add CanReachToInterernetCtxProvder and useCanReachToInternetCtx component's documentation
+
## 2.0.0-canary.3
### Minor Changes
diff --git a/package.json b/package.json
index 6e2c874..a06f8c9 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "classic-react-hooks",
- "version": "2.0.0-canary.3",
+ "version": "2.0.0-canary.4",
"description": "A great collection of React utility hooks",
"keywords": [
"classic-react-hooks",
From 51e96f8e25bf0d0a6d888535aa2f4e0498788eab Mon Sep 17 00:00:00 2001
From: Ashish-simpleCoder
Date: Sun, 6 Jul 2025 23:12:20 +0530
Subject: [PATCH 24/81] doc: update docs for hooks and home page for the
documentation site
---
.changeset/clean-coats-occur.md | 5 ++++
.../CanReachToInternetCtxProvider.md | 2 +-
apps/doc/hooks/use-can-reach-to-internet.md | 28 +++++++++----------
apps/doc/hooks/use-copy-to-clipboard.md | 10 +++----
apps/doc/hooks/use-counter.md | 16 +++++------
apps/doc/hooks/use-debounced-fn.md | 14 +++++-----
apps/doc/hooks/use-event-listener.md | 16 +++++------
apps/doc/hooks/use-intersection-observer.md | 16 +++++------
apps/doc/hooks/use-interval-effect.md | 12 ++++----
apps/doc/hooks/use-local-storage.md | 14 +++++-----
apps/doc/hooks/use-on-mount-effect.md | 10 +++----
apps/doc/hooks/use-outside-click.md | 12 ++++----
apps/doc/hooks/use-synced-effect.md | 16 +++++------
apps/doc/hooks/use-synced-ref.md | 16 +++++------
apps/doc/hooks/use-throttled-fn.md | 14 +++++-----
apps/doc/hooks/use-timeout-effect.md | 12 ++++----
apps/doc/hooks/use-window-resize.md | 20 ++++++-------
apps/doc/index.md | 6 +++-
18 files changed, 123 insertions(+), 116 deletions(-)
create mode 100644 .changeset/clean-coats-occur.md
diff --git a/.changeset/clean-coats-occur.md b/.changeset/clean-coats-occur.md
new file mode 100644
index 0000000..321d05b
--- /dev/null
+++ b/.changeset/clean-coats-occur.md
@@ -0,0 +1,5 @@
+---
+'classic-react-hooks': minor
+---
+
+Doc update
diff --git a/apps/doc/components/CanReachToInternetCtxProvider.md b/apps/doc/components/CanReachToInternetCtxProvider.md
index a0cf50a..b911ece 100644
--- a/apps/doc/components/CanReachToInternetCtxProvider.md
+++ b/apps/doc/components/CanReachToInternetCtxProvider.md
@@ -6,7 +6,7 @@ outline: deep
A React Context provider and hook for sharing internet connectivity status across your component tree without prop drilling. Built on top of the `useCanReachToInternet` hook to provide centralized connectivity monitoring.
-### Features
+## Features
- **Centralized connectivity state:** Share connectivity status across your entire app
- **No prop drilling:** Access connectivity data from any component in the tree
diff --git a/apps/doc/hooks/use-can-reach-to-internet.md b/apps/doc/hooks/use-can-reach-to-internet.md
index 99ada18..2d477da 100644
--- a/apps/doc/hooks/use-can-reach-to-internet.md
+++ b/apps/doc/hooks/use-can-reach-to-internet.md
@@ -6,7 +6,7 @@ outline: deep
A comprehensive React hook for monitoring internet connectivity status that goes beyond basic online/offline detection by actually testing network reachability.
-### Features
+## Features
- **Real connectivity testing:** Performs actual HTTP requests to verify internet access
- **Dual-layer detection:** Combines browser's `navigator.onLine` with network reachability tests
@@ -14,13 +14,13 @@ A comprehensive React hook for monitoring internet connectivity status that goes
- **Manual control:** Start/stop polling and force connectivity checks on demand
- **Cleanup handling:** Proper cleanup of network requests and timers to prevent memory leaks
-### Parameters
+## Parameters
| Parameter | Type | Required | Default Value | Description |
| --------- | :---------------------------------: | :------: | :-----------: | -------------------------------------------------- |
| options | [CanReachToInternetOptions](#types) | ❌ | {} | Configuration object for customizing hook behavior |
-#### Parameter Types
+### Parameter Types
```ts
type CanReachToInternetOptions = {
@@ -35,7 +35,7 @@ type CanReachToInternetOptions = {
type CanReachToInternetBoolean = boolean
```
-### Returns
+## Returns
| Property | Type | Description |
| ----------------------------- | --------------- | -------------------------------------------------------------------------------- |
@@ -49,9 +49,9 @@ type CanReachToInternetBoolean = boolean
| `forceCheckNetwork` | `() => void` | Function to manually trigger a connectivity check |
| `getCanReachToInternetStatus` | `() => boolean` | Function to get current internet reachability status |
-### Usage Examples
+## Usage Examples
-#### Basic Network query
+### Basic Network query
```ts
import { useCanReachToInternet } from 'classic-react-hooks'
@@ -69,7 +69,7 @@ function NetworkStatus() {
}
```
-#### Conditional Rendering Based on Connectivity
+### Conditional Rendering Based on Connectivity
```ts
import { useCanReachToInternet } from 'classic-react-hooks'
@@ -94,15 +94,13 @@ function DataFetchingComponent() {
}
```
-### Problem It Solves
+## Problem It Solves
----
-
-#### The Problem with `navigator.onLine`
+### The Problem with `navigator.onLine`
`navigator.onLine` only tells you if the browser thinks it's connected to a network, not if it can actually reach the internet.
-##### Common Scenarios Where `navigator.onLine` Fails
+#### Common Scenarios Where `navigator.onLine` Fails
- **Limited Connectivity:** Your device is connected to a router, but the router has no internet connection. The browser sees the local network connection and reports online status as true.
- **Network Issues:** DNS problems or ISP outages where you have network connection but can't reach to external servers.
@@ -110,7 +108,7 @@ function DataFetchingComponent() {
---
-#### How `useCanReachToInternet` solve these problems
+### How `useCanReachToInternet` solve these problems
It provides two layers of connectivity detection
@@ -118,13 +116,13 @@ It provides two layers of connectivity detection
- **`canReachToInternet`:** Actual internet reachability (via real HTTP requests to a test server)
- **`isFullyConnected`:** Both conditions must be true for genuine internet access
-### Common Use Cases
+## Common Use Cases
- User experience: Show connection status, disable features when offline
- Error handling: Distinguish between network errors and server errors
- Auto-retry logic: Retry failed requests when connectivity is restored
-### Important Notes
+## Important Notes
- Performance Considerations:
- Network polling makes regular HTTP requests - use appropriate intervals
diff --git a/apps/doc/hooks/use-copy-to-clipboard.md b/apps/doc/hooks/use-copy-to-clipboard.md
index 6bbbba8..0e99ac8 100644
--- a/apps/doc/hooks/use-copy-to-clipboard.md
+++ b/apps/doc/hooks/use-copy-to-clipboard.md
@@ -6,13 +6,13 @@ outline: deep
- A hook for copying the data in the clipboard with success and error callbacks.
-### Parameters
+## Parameters
| Parameter | Type | Required | Default Value | Description |
| --------- | :---------------------: | :------: | :-----------: | -------------------------------- |
| Props | [Props](#parametertype) | ❌ | - | Pass success and error callbacks |
-#### ParameterType
+### ParameterType
```ts
type Props = {
@@ -24,17 +24,17 @@ type OnSuccess = () => void
type OnError = (err: Error) => void
```
-### Returns
+## Returns
[`CopyToClipboardFn`](#returntype) - A function for copying the data into clipboard
-#### ReturnType
+### ReturnType
```ts
type CopyToClipboardFn = (data: string, onSuccess?: OnSuccess, onError?: OnError) => Promise
```
-### Usage
+## Usage
```ts
import { useState } from 'react'
diff --git a/apps/doc/hooks/use-counter.md b/apps/doc/hooks/use-counter.md
index 259c60f..66332f0 100644
--- a/apps/doc/hooks/use-counter.md
+++ b/apps/doc/hooks/use-counter.md
@@ -8,11 +8,11 @@ A Hook for Fun
A type-safe React hook for managing counter state with customizable step values and dynamic property naming.
-#### Features
+## Features
- Find out yourself buddy
-### Parameters
+## Parameters
| Parameter | Type | Required | Default Value | Description |
| ------------ | ------ | :------: | :-----------: | ------------------------------------------------------------------------- |
@@ -21,25 +21,25 @@ A type-safe React hook for managing counter state with customizable step values
| initialValue | number | ❌ | 0 | Initial value for the counter. |
| stepper | number | ❌ | 1 | Amount to increment/decrement by on each operation. |
-### Returns
+## Returns
Returns a type-safe object with dynamically named properties:
-#### Without key (default):
+### Without key (default):
- `counter:` number - Current counter value
- `incrementCounter:` () => void - Function to increment counter
- `decrementCounter:` () => void - Function to decrement counter
-#### With key (e.g., "user"):
+### With key (e.g., "user"):
- `userCounter:` number - Current counter value
- `incrementUserCounter:` () => void - Function to increment counter
- `decrementUserCounter:` () => void - Function to decrement counter
-### Usage Examples
+## Usage Examples
-#### Basic Counter
+### Basic Counter
```ts
import { useCounter } from 'classic-react-hooks'
@@ -62,7 +62,7 @@ export default function YourComponent() {
}
```
-#### Named Counter with Custom Step
+### Named Counter with Custom Step
```ts
import { useCounter } from 'classic-react-hooks'
diff --git a/apps/doc/hooks/use-debounced-fn.md b/apps/doc/hooks/use-debounced-fn.md
index 7046ea2..a86c1dc 100644
--- a/apps/doc/hooks/use-debounced-fn.md
+++ b/apps/doc/hooks/use-debounced-fn.md
@@ -8,7 +8,7 @@ A React hook that returns a debounced version of any function, delaying its exec
Perfect for optimizing performance in scenarios like search inputs, API calls, or resize handlers.
-### Features
+## Features
- **Debouncing Functionality:** Delaying function execution until after a specified period of inactivity. Calling the function again before the delay expires, the previous call is cancelled and the timer resets.
- **Configurable Delay:** You can specify a custom delay period, with a sensible default of 300ms.
@@ -16,20 +16,20 @@ Perfect for optimizing performance in scenarios like search inputs, API calls, o
- **Performance optimized:** Prevents excessive function calls
- **Auto cleanup:** Automatically clears timers on component unmount and on delay prop change
-### Parameters
+## Parameters
| Parameter | Type | Required | Default Value | Description |
| ---------------- | :------: | :------: | :-----------: | ----------------------------------------------------- |
| callbackToBounce | Function | ✅ | - | The function to be debounced |
| delay | number | ❌ | 300 | Delay in milliseconds before the function is executed |
-### Returns
+## Returns
- Returns a debounced version of the provided function that will only execute after the specified delay has passed since the last invocation.
-### Usage Examples
+## Usage Examples
-#### Basic debouncing
+### Basic debouncing
```ts
import { useState, useEffect } from 'react'
@@ -77,13 +77,13 @@ export default function SearchInput() {
}
```
-### Common Use Cases
+## Common Use Cases
- Delay API calls until user stops typing
- Validate fields after user pauses input
- Prevent excessive API calls
-### Alternative: Non-React Usage
+## Alternative: Non-React Usage
For use outside of React components, use the standalone wrapper:
diff --git a/apps/doc/hooks/use-event-listener.md b/apps/doc/hooks/use-event-listener.md
index 569654a..f225102 100644
--- a/apps/doc/hooks/use-event-listener.md
+++ b/apps/doc/hooks/use-event-listener.md
@@ -6,7 +6,7 @@ outline: deep
A React hook that provides a declarative way to add DOM event listeners with automatic cleanup.
-### Features
+## Features
- **Auto cleanup:** Events are automatically removed on unmount or dependency changes
- **Reactive:** The hook re-evaluates and potentially re-attaches listeners when any dependency changes (target, event, options)
@@ -14,7 +14,7 @@ A React hook that provides a declarative way to add DOM event listeners with aut
- **Performance:** Event listeners are only attached when all conditions are met: target exists, handler is provided, and `shouldInjectEvent` is true
- **Standard options:** Full support for all `AddEventListenerOptions` (capture, once, passive, signal)
-### Parameters
+## Parameters
| Parameter | Type | Required | Default Value | Description |
| --------- | :-----------------: | :------: | :-----------: | ------------------------------------------------ |
@@ -24,7 +24,7 @@ A React hook that provides a declarative way to add DOM event listeners with aut
| options | [EvOptions](#types) | ❌ | undefined | Event listener options and feature flags |
| |
-#### Types
+### Types
```ts
export type EvTarget = () => EventTarget | null
@@ -42,9 +42,9 @@ export interface EvOptions extends AddEventListenerOptions {
}
```
-### Usage Examples
+## Usage Examples
-#### Basic Click Handler
+### Basic Click Handler
```ts
import { useRef } from 'react'
@@ -65,7 +65,7 @@ export default function ClickExample() {
}
```
-#### Window Events
+### Window Events
```ts
import { useEventListener } from 'classic-react-hooks'
@@ -83,7 +83,7 @@ export default function WindowExample() {
}
```
-#### Conditional Event Listening
+### Conditional Event Listening
```ts
import { useState } from 'react'
@@ -112,6 +112,6 @@ export default function ConditionalExample() {
}
```
-### Common Use Cases
+## Common Use Cases
- Adding dom events (e.g 'click', 'keydown', 'resize')
diff --git a/apps/doc/hooks/use-intersection-observer.md b/apps/doc/hooks/use-intersection-observer.md
index 7adf4a9..688fd28 100644
--- a/apps/doc/hooks/use-intersection-observer.md
+++ b/apps/doc/hooks/use-intersection-observer.md
@@ -6,7 +6,7 @@ outline: deep
A React hook that provides a declarative way to observe multiple elements with the Intersection Observer API, returning their visibility states with advanced triggering options.
-### Features
+## Features
- **Multiple targets:** Observe multiple elements simultaneously
- **Flexible triggering:** Control whether elements trigger once or continuously
@@ -17,7 +17,7 @@ A React hook that provides a declarative way to observe multiple elements with t
- **Performance:** Elements with `only_trigger_once: true` are automatically unobserved after first intersection
- **Per-element control:** Use `only_trigger_once` as an array to control trigger behavior per element
-### Parameters
+## Parameters
| Parameter | Type | Required | Default | Description |
| -------------- | :------------------------------------: | :------: | :---------------------------: | ------------------------------------------------------- |
@@ -26,7 +26,7 @@ A React hook that provides a declarative way to observe multiple elements with t
| onIntersection | (target: Element) => void | ❌ | undefined | Callback executed when an element becomes visible |
| |
-#### Types
+### Types
```ts
export type IntersectionObserverTarget = () => Element | null
@@ -43,13 +43,13 @@ export interface IntersectionOptions extends IntersectionObserverInit {
}
```
-### Returns
+## Returns
Returns an array of boolean values (`Array`) where each boolean represents whether the corresponding target element is currently intersecting (visible) or not.
-### Usage Examples
+## Usage Examples
-#### Basic Usage - Multiple Elements
+### Basic Usage - Multiple Elements
```ts
import { useRef } from 'react'
@@ -222,11 +222,11 @@ export default function WithCallback() {
}
```
-### Important Notes
+## Important Notes
- If IntersectionObserver is not supported, a warning is logged and the hook gracefully degrades.
-### Common Use Cases
+## Common Use Cases
- Lazy loading images or content
- Triggering animations on scroll
diff --git a/apps/doc/hooks/use-interval-effect.md b/apps/doc/hooks/use-interval-effect.md
index 5c3ecb6..aa81d47 100644
--- a/apps/doc/hooks/use-interval-effect.md
+++ b/apps/doc/hooks/use-interval-effect.md
@@ -6,30 +6,30 @@ outline: deep
A React hook that executes a callback function at regular intervals, similar to `setInterval` but with additional control methods for clearing and restarting the timer.
-### Features
+## Features
- **Scheduled execution:** Executes a callback with a fixed time delay between each call
- **Flexible:** Provides methods to clear or restart the timer
- **Automatic Cleanup:** Automatically cleans up timer on component unmount
- **Syncronization:** Syncs with the latest callback and timeout values
-### Parameters
+## Parameters
| Parameter | Type | Required | Default Value | Description |
| --------- | :------: | :------: | :-----------: | ------------------------------------------------ |
| handler | Function | ✅ | - | Callback function executed at each interval |
| interval | number | ❌ | 100 | Time in milliseconds between callback executions |
-### Returns
+## Returns
- Returns an object with control methods:
- `clearTimer` : `() => void` Cancels the current interval, preventing the handler from executing
- `restartTimer` : `() => void` Clears the current timer and starts a new one. Optionally accepts a new interval value
-### Usage
+## Usage
-#### Basic example
+### Basic example
```ts
import { useState } from 'react'
@@ -55,7 +55,7 @@ export default function Counter() {
}
```
-### Common Use Cases
+## Common Use Cases
- Countdown timers
- Real-time updates (clocks, progress bars)
diff --git a/apps/doc/hooks/use-local-storage.md b/apps/doc/hooks/use-local-storage.md
index 43559ca..619603f 100644
--- a/apps/doc/hooks/use-local-storage.md
+++ b/apps/doc/hooks/use-local-storage.md
@@ -8,7 +8,7 @@ A React hook that synchronizes state with localStorage, providing `persistent` s
Works exactly like `useState` but automatically persists data to localStorage.
-### Features
+## Features
- **Automatic sync:** Automatic localStorage synchronization
- **Persistence updates:** Seamless state updates with persistence
@@ -16,23 +16,23 @@ Works exactly like `useState` but automatically persists data to localStorage.
- **Compatible API:** `useState` compatible API
- **Automatic parsing:** JSON serialization/deserialization
-### Parameters
+## Parameters
| Parameter | Type | Required | Default Value | Description |
| ------------ | :----: | :------: | :-----------: | ----------------------------------------- |
| key | string | ✅ | - | Unique key for localStorage item |
| defaultValue | any | ❌ | undefined | Initial value when no stored value exists |
-### Returns
+## Returns
- It returns an array containing:
1. `state:` Current value (type inferred from defaultValue)
2. `setState:` State setter function (identical to useState setter)
-### Usage Examples
+## Usage Examples
-#### Basic User Preferences
+### Basic User Preferences
```ts
import { useLocalStorage } from 'classic-react-hooks'
@@ -120,14 +120,14 @@ function ProfileForm() {
}
```
-### Important Notes
+## Important Notes
- **Automatic Serialization:** Data is automatically serialized to JSON when storing.
- **Synchronous Updates:** State updates are synchronous and immediately persisted.
- **No storage events:** Changes in one tab don't automatically sync to other tabs (consider storage events for that).
- **Fallback value:** Always provide default values for SSR fallback.
-### Common Use Cases
+## Common Use Cases
- Theme preferences (dark/light mode)
- Form draft saving (auto-save functionality)
diff --git a/apps/doc/hooks/use-on-mount-effect.md b/apps/doc/hooks/use-on-mount-effect.md
index 242351c..01e1fab 100644
--- a/apps/doc/hooks/use-on-mount-effect.md
+++ b/apps/doc/hooks/use-on-mount-effect.md
@@ -8,15 +8,15 @@ A React hook that executes a callback function only once after the component mou
This hook is perfect for initialization logic that should run exactly once when a component first renders.
-### Parameters
+## Parameters
| Parameter | Type | Required | Default Value | Description |
| --------- | :------------------: | :------: | :-----------: | ----------------------------------------------------------------------------- |
| cb | React.EffectCallback | ✅ | - | Callback function to execute once after mount. Can return a cleanup function. |
-### Usage Examples
+## Usage Examples
-#### Basic Usage - Initialization Logic
+### Basic Usage - Initialization Logic
```ts
import { useOnMountEffect } from 'classic-react-hooks'
@@ -30,7 +30,7 @@ export default function YourComponent() {
}
```
-### Comparison with useEffect
+## Comparison with useEffect
| Scenario | useEffect(cb, []) | useOnMountEffect(cb) |
| ------------------- | -------------------- | ------------------------- |
@@ -40,7 +40,7 @@ export default function YourComponent() {
| TypeScript support | ✅ Yes | ✅ Yes |
| Cleanup support | ✅ Yes | ✅ Yes |
-### Common Use Cases
+## Common Use Cases
- Setting up initial state or configuration
- Any one-time setup that shouldn't repeat after component mount
diff --git a/apps/doc/hooks/use-outside-click.md b/apps/doc/hooks/use-outside-click.md
index 3be40ca..75ceb56 100644
--- a/apps/doc/hooks/use-outside-click.md
+++ b/apps/doc/hooks/use-outside-click.md
@@ -8,13 +8,13 @@ A React hook that detects outside click for specified element and triggers the g
Perfect for implementing modals, dropdowns and other UI components that need to be closed when users click outside of them.
-### Features
+## Features
- **Precise trigger:** Precise outside click detection
- **Performance:** Optimized with capture phase events
- **Underlying hook:** At its core, it uses `useEventListener` hook
-### Parameters
+## Parameters
| Parameter | Type | Required | Default Value | Description |
| --------- | :-----------------: | :------: | :-----------: | ------------------------------------------------ |
@@ -22,7 +22,7 @@ Perfect for implementing modals, dropdowns and other UI components that need to
| handler | [EvHandler](#types) | ❌ | undefined | Callback executed on outside click |
| options | [EvOptions](#types) | ❌ | undefined | Event listener options and feature flags |
-#### Types
+### Types
```ts
type EvTarget = () => EventTarget | null
@@ -39,9 +39,9 @@ interface EvOptions extends AddEventListenerOptions {
}
```
-### Usage Examples
+## Usage Examples
-#### Modal Component
+### Modal Component
```ts
import { useRef, useState } from 'react'
@@ -131,7 +131,7 @@ function DynamicTarget() {
}
```
-### Common Use Cases
+## Common Use Cases
- Modal dialogs - Close when clicking backdrop
- Dropdown menus - Hide when clicking elsewhere
diff --git a/apps/doc/hooks/use-synced-effect.md b/apps/doc/hooks/use-synced-effect.md
index 545032f..8c483c0 100644
--- a/apps/doc/hooks/use-synced-effect.md
+++ b/apps/doc/hooks/use-synced-effect.md
@@ -8,23 +8,23 @@ A React hook that executes a callback when dependencies change, similar to `useE
This is particularly useful when you want to respond to state changes without triggering side effects during the component's first render.
-### Features
+## Features
- **Skip initial mount:** Skipping the callback on initial mount
- **Reactive:** Running the callback only when dependencies actually change
- **React StrictMode:** Handling React StrictMode double execution correctly
- **Flexible:** Supporting cleanup functions just like useEffect
-### Parameters
+## Parameters
| Parameter | Type | Required | Default Value | Description |
| --------- | :------------------: | :------: | :-----------: | ------------------------------------------------------------------------------------ |
| cb | React.EffectCallback | ✅ | - | Callback function to execute when dependencies change. Can return a cleanup function |
| deps | React.DependencyList | ❌ | [] | Array of dependencies to watch for changes |
-### Usage Examples
+## Usage Examples
-#### Basic Usage - Responding to State Changes
+### Basic Usage - Responding to State Changes
```ts
import { useState } from 'react'
@@ -45,7 +45,7 @@ export default function YourComponent() {
}
```
-#### With Cleanup Function
+### With Cleanup Function
```ts
import { useState } from 'react'
@@ -78,7 +78,7 @@ function SearchComponent() {
}
```
-### Comparison with useEffect
+## Comparison with useEffect
| Scenario | useEffect | useSyncedEffect |
| ------------------- | ---------------- | -------------------- |
@@ -87,10 +87,10 @@ function SearchComponent() {
| Cleanup support | ✅ Yes | ✅ Yes |
| StrictMode handling | ⚠️ May run twice | ✅ Handles correctly |
-### Important notes
+## Important notes
- Empty dependency array [] means the effect will never run (since there are no dependencies to change)
-### Common Use Cases
+## Common Use Cases
- Use everywhere just like `useEffect`
diff --git a/apps/doc/hooks/use-synced-ref.md b/apps/doc/hooks/use-synced-ref.md
index 949d3e4..ee489af 100644
--- a/apps/doc/hooks/use-synced-ref.md
+++ b/apps/doc/hooks/use-synced-ref.md
@@ -8,25 +8,25 @@ A React hook that creates a ref that automatically stays in sync with the provid
This eliminates the need to manually update refs and helps avoid stale closure issues in callbacks and effects.
-### Features
+## Features
- **Reactive:** Automatic synchronization with any value
- **Prevent State Closure:** Prevents stale closure problems
- **No Re-render:** Zero re-renders - purely ref-based
-### Parameters
+## Parameters
| Parameter | Type | Required | Default Value | Description |
| --------- | :--: | :------: | :-----------: | ------------------------------------------------- |
| value | any | ✅ | - | Any value to be tracked and kept in sync with ref |
-### Returns
+## Returns
- Returns a `React.MutableRefObject` that always contains the latest value of the provided state.
-### Usage
+## Usage
-#### Basic Example
+### Basic Example
```ts
import { useState } from 'react'
@@ -54,9 +54,9 @@ export default function Counter() {
}
```
-### Problem It Solves
+## Problem It Solves
-#### The Stale Closure Problem
+### The Stale Closure Problem
In React, when you use hooks like useEffect, useCallback, or setTimeout with dependency arrays, you often encounter stale closure issues:
@@ -96,7 +96,7 @@ function SolvedComponent() {
}
```
-### Common Use Cases
+## Common Use Cases
- Accessing latest state in intervals/timeouts
- Event handlers that need current state
diff --git a/apps/doc/hooks/use-throttled-fn.md b/apps/doc/hooks/use-throttled-fn.md
index e944dad..2241208 100644
--- a/apps/doc/hooks/use-throttled-fn.md
+++ b/apps/doc/hooks/use-throttled-fn.md
@@ -10,27 +10,27 @@ Throttling ensures that the function is called at most once per specified time i
This is particularly useful for performance optimization in scenarios like handling rapid user input, scroll events, or API calls.
-### Features
+## Features
- **Throttling Functionality:** Limits function execution to at most once per specified time period
- **Configurable Delay:** Accepts a custom delay period with a default of 300ms
- **Dynamic Props Updates:** The hook properly handles updates to both the callback function and delay value during re-renders without losing the debouncing behavior
- **Performance optimized:** Prevents excessive function calls
-### Parameters
+## Parameters
| Parameter | Type | Required | Default Value | Description |
| ------------------ | :------: | :------: | :-----------: | --------------------------------------------------------- |
| callbackToThrottle | Function | ✅ | - | The callback function that should be throttled |
| delay | number | ❌ | 300 | Delay in milliseconds between allowed function executions |
-### Returns
+## Returns
- Returns a throttled version of the provided callback function that maintains the same signature and behavior, but with throttling applied.
-### Usage Examples
+## Usage Examples
-#### Basic API throttling
+### Basic API throttling
```ts
import { useState } from 'react'
@@ -70,13 +70,13 @@ export default function AutoSave() {
}
```
-### Common Use Cases
+## Common Use Cases
- Real-time form validation
- Prevent excessive API calls
- Throttle scroll event processing
-### Alternative: Non-React Usage
+## Alternative: Non-React Usage
For use outside of React components, use the standalone wrapper:
diff --git a/apps/doc/hooks/use-timeout-effect.md b/apps/doc/hooks/use-timeout-effect.md
index 1c18af1..0ceddae 100644
--- a/apps/doc/hooks/use-timeout-effect.md
+++ b/apps/doc/hooks/use-timeout-effect.md
@@ -6,30 +6,30 @@ outline: deep
A React hook that fires a provided callback after a specified timeout, similar to `setTimeout`, but with additional control methods for clearing and restarting the timer.
-### Features
+## Features
- **Scheduled execution:** Executes a callback after a specified delay
- **Flexible:** Provides methods to clear or restart the timer
- **Automatic Cleanup:** Automatically cleans up timer on component unmount
- **Syncronization:** Syncs with the latest callback and timeout values
-### Parameters
+## Parameters
| Parameter | Type | Required | Default Value | Description |
| --------- | :------: | :------: | :-----------: | ------------------------------------------------------ |
| handler | Function | ✅ | - | The callback function to execute after the timeout |
| timeout | number | ❌ | 100 | The delay in milliseconds before executing the handler |
-### Returns
+## Returns
- Returns an object with control methods:
- `clearTimer` : `() => void` Cancels the current timeout, preventing the handler from executing
- `restartTimer` : `() => void` Clears the current timer and starts a new one. Optionally accepts a new timeout value
-### Usage Examples
+## Usage Examples
-#### Basic use
+### Basic use
```ts
import { useState } from 'react'
@@ -49,7 +49,7 @@ export default function BasicExample() {
}
```
-### Important Notes
+## Important Notes
- The hook uses `useSyncedRef` to ensure the latest callback and timeout values are always used.
- The restartTimer method can accept an optional new timeout value, otherwise it uses the original timeout.
diff --git a/apps/doc/hooks/use-window-resize.md b/apps/doc/hooks/use-window-resize.md
index ce0224f..f8377e7 100644
--- a/apps/doc/hooks/use-window-resize.md
+++ b/apps/doc/hooks/use-window-resize.md
@@ -8,14 +8,14 @@ A React hook that evaluates a callback function on window resize events and retu
Perfect for responsive behavior based on window dimensions.
-### Features
+## Features
- **Custom handler:** Execute custom logic on window resize
- **Reactive:** Automatic re-evaluation and state updates
- **Configurable:** Configurable default values and event injection
- **Underlying hook:** At its core, it uses `useEventListener` hook
-### Parameters
+## Parameters
| Parameter | Type | Required | Default Value | Description |
| --------- | :---------------: | :------: | :-----------: | ------------------------------------------- |
@@ -23,20 +23,20 @@ Perfect for responsive behavior based on window dimensions.
| options | [Options](#types) | ❌ | undefined | Configuration options |
| |
-#### Types
+### Types
```ts
type Handler = () => T
type Options = { shouldInjectEvent?: boolean; defaultValue?: T }
```
-### Returns
+## Returns
- Returns the current result of the `handler` function, updated whenever the window is resized.
-### Usage Examples
+## Usage Examples
-#### Basic Responsive Breakpoints
+### Basic Responsive Breakpoints
```ts
import { useWindowResize } from 'classic-react-hooks'
@@ -98,9 +98,9 @@ function ComponentWithDefault() {
}
```
-### Advanced Usage
+## Advanced Usage
-#### Debounced Resize Handler
+### Debounced Resize Handler
```ts
import { useMemo } from 'react'
@@ -122,11 +122,11 @@ function ExpensiveCalculation() {
}
```
-### Important Notes
+## Important Notes
- Initial value is determined by either `defaultValue` or calling `handler()` immediately.
-### Common Use Cases
+## Common Use Cases
- Creating dynamic layouts
- Toggling element visibility based on window dimension
diff --git a/apps/doc/index.md b/apps/doc/index.md
index 4e4cbe5..1196d13 100644
--- a/apps/doc/index.md
+++ b/apps/doc/index.md
@@ -16,7 +16,7 @@ hero:
features:
- icon: 🚀
title: Essential React Hooks
- details: Reusable hooks for efficient feature creation
+ details: Comprehensive collection of custom hooks
- icon:
title: Type-safe
@@ -25,4 +25,8 @@ features:
- icon: 🌏
title: Open-Source
details: 100% open-source — contributions are welcome!
+
+ - icon: 🌲
+ title: Tree-Shakeble
+ details: Lightweight, minimal bundle sizes
---
From ee642128d52550310fe5bef5a9ee18ad6ed443f1 Mon Sep 17 00:00:00 2001
From: "github-actions[bot]"
<41898282+github-actions[bot]@users.noreply.github.com>
Date: Sun, 6 Jul 2025 17:48:39 +0000
Subject: [PATCH 25/81] Version Packages (canary)
---
.changeset/pre.json | 1 +
CHANGELOG.md | 6 ++++++
package.json | 2 +-
3 files changed, 8 insertions(+), 1 deletion(-)
diff --git a/.changeset/pre.json b/.changeset/pre.json
index 99a70b7..97ebd35 100644
--- a/.changeset/pre.json
+++ b/.changeset/pre.json
@@ -5,6 +5,7 @@
"classic-react-hooks": "1.4.0"
},
"changesets": [
+ "clean-coats-occur",
"cuddly-melons-turn",
"good-vans-impress",
"icy-boats-make",
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 586c4f2..4ba9550 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,11 @@
# classic-react-hooks
+## 2.0.0-canary.5
+
+### Minor Changes
+
+- 51e96f8: Doc update
+
## 2.0.0-canary.4
### Minor Changes
diff --git a/package.json b/package.json
index a06f8c9..58ffee1 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "classic-react-hooks",
- "version": "2.0.0-canary.4",
+ "version": "2.0.0-canary.5",
"description": "A great collection of React utility hooks",
"keywords": [
"classic-react-hooks",
From c20ae22f6f55d195ccda15662b1711db50edcea3 Mon Sep 17 00:00:00 2001
From: Ashish-simpleCoder
Date: Thu, 10 Jul 2025 21:55:53 +0530
Subject: [PATCH 26/81] build: include src folder in npm publish
---
.changeset/fresh-hounds-move.md | 5 +++++
apps/doc/index.md | 2 +-
package.json | 4 +++-
3 files changed, 9 insertions(+), 2 deletions(-)
create mode 100644 .changeset/fresh-hounds-move.md
diff --git a/.changeset/fresh-hounds-move.md b/.changeset/fresh-hounds-move.md
new file mode 100644
index 0000000..385ac7b
--- /dev/null
+++ b/.changeset/fresh-hounds-move.md
@@ -0,0 +1,5 @@
+---
+'classic-react-hooks': patch
+---
+
+Include src folder in npm publish
diff --git a/apps/doc/index.md b/apps/doc/index.md
index 1196d13..87156f7 100644
--- a/apps/doc/index.md
+++ b/apps/doc/index.md
@@ -27,6 +27,6 @@ features:
details: 100% open-source — contributions are welcome!
- icon: 🌲
- title: Tree-Shakeble
+ title: Tree-Shakeable
details: Lightweight, minimal bundle sizes
---
diff --git a/package.json b/package.json
index 58ffee1..364c01b 100644
--- a/package.json
+++ b/package.json
@@ -25,7 +25,9 @@
"module": "dist/index.mjs",
"typings": "dist/index.d.ts",
"files": [
- "dist"
+ "dist",
+ "src",
+ "tsup.config.ts"
],
"scripts": {
"build": "pnpm clean && tsup",
From 39ece032fa715cb709f00aa2acc1caae1233d3eb Mon Sep 17 00:00:00 2001
From: Ashish Prajapati <62009244+Ashish-simpleCoder@users.noreply.github.com>
Date: Thu, 10 Jul 2025 22:04:55 +0530
Subject: [PATCH 27/81] Version Packages (canary) (#77)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
---
.changeset/pre.json | 1 +
CHANGELOG.md | 6 ++++++
package.json | 2 +-
3 files changed, 8 insertions(+), 1 deletion(-)
diff --git a/.changeset/pre.json b/.changeset/pre.json
index 97ebd35..ecec17e 100644
--- a/.changeset/pre.json
+++ b/.changeset/pre.json
@@ -7,6 +7,7 @@
"changesets": [
"clean-coats-occur",
"cuddly-melons-turn",
+ "fresh-hounds-move",
"good-vans-impress",
"icy-boats-make",
"pink-pugs-eat",
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 4ba9550..8833bfa 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,11 @@
# classic-react-hooks
+## 2.0.0-canary.6
+
+### Patch Changes
+
+- c20ae22: Include src folder in npm publish
+
## 2.0.0-canary.5
### Minor Changes
diff --git a/package.json b/package.json
index 364c01b..8de1f08 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "classic-react-hooks",
- "version": "2.0.0-canary.5",
+ "version": "2.0.0-canary.6",
"description": "A great collection of React utility hooks",
"keywords": [
"classic-react-hooks",
From cc71e9c191927a49246d3e35fa700e1b85e6b757 Mon Sep 17 00:00:00 2001
From: Ashish-simpleCoder
Date: Sun, 13 Jul 2025 17:50:41 +0530
Subject: [PATCH 28/81] Update docs
---
.changeset/violet-cherries-applaud.md | 5 ++
apps/doc/hooks/use-copy-to-clipboard.md | 100 ++++++++++++++++--------
src/lib/use-debounced-fn/index.tsx | 46 +++++++++++
3 files changed, 118 insertions(+), 33 deletions(-)
create mode 100644 .changeset/violet-cherries-applaud.md
diff --git a/.changeset/violet-cherries-applaud.md b/.changeset/violet-cherries-applaud.md
new file mode 100644
index 0000000..4dc67d5
--- /dev/null
+++ b/.changeset/violet-cherries-applaud.md
@@ -0,0 +1,5 @@
+---
+'classic-react-hooks': patch
+---
+
+Update docs for copy-to-clipboard and deboucned-fn hook
diff --git a/apps/doc/hooks/use-copy-to-clipboard.md b/apps/doc/hooks/use-copy-to-clipboard.md
index 0e99ac8..0351f3f 100644
--- a/apps/doc/hooks/use-copy-to-clipboard.md
+++ b/apps/doc/hooks/use-copy-to-clipboard.md
@@ -4,61 +4,95 @@ outline: deep
# use-copy-to-clipboard
-- A hook for copying the data in the clipboard with success and error callbacks.
+A React hook that provides a simple and reliable way to copy text to the clipboard with success and error handling callbacks.
+
+## Features
+
+- **Clipboard API Support:** Uses the modern `navigator.clipboard` API for secure clipboard access
+- **Fallback Handling:** Gracefully handles cases where clipboard API is not available
+- **Success/Error Callbacks:** Built-in success and error handling with customizable callbacks
+- **Flexible Configuration:** Configure global callbacks via props or override per-call
+- **Performance Optimized:** Zero re-renders - purely ref-based
## Parameters
-| Parameter | Type | Required | Default Value | Description |
-| --------- | :---------------------: | :------: | :-----------: | -------------------------------- |
-| Props | [Props](#parametertype) | ❌ | - | Pass success and error callbacks |
+| Parameter | Type | Required | Default Value | Description |
+| --------- | :---------------------------: | :------: | :-----------: | --------------------------------- |
+| onSuccess | [OnSuccess](#parameter-types) | ❌ | - | Default success callback function |
+| onError | [OnError](#parameter-types) | ❌ | - | Default error callback function |
-### ParameterType
+### Parameter Types
```ts
-type Props = {
- onSuccess?: OnSuccess
- onError?: onError
-}
-
type OnSuccess = () => void
type OnError = (err: Error) => void
```
## Returns
-[`CopyToClipboardFn`](#returntype) - A function for copying the data into clipboard
+- `copyToClipboard` - Handler function to copy text data
-### ReturnType
+### Return Types
```ts
type CopyToClipboardFn = (data: string, onSuccess?: OnSuccess, onError?: OnError) => Promise
```
-## Usage
+## Usage Examples
+
+### Basic usage
```ts
import { useState } from 'react'
import { useCopyToClipboard } from 'classic-react-hooks'
-export default function YourComponent() {
- const [data, setData] = useState('')
- const copyToClipboard = useCopyToClipboard()
-
- return (
-
- setData(e.target.value)} />
-
-
- )
+export default function CopyButton() {
+ const [copied, setCopied] = useState(false)
+
+ const copyToClipboard = useCopyToClipboard({
+ onSuccess: () => {
+ setCopied(true)
+ setTimeout(() => setCopied(false), 2000)
+ },
+ onError: (error) => {
+ console.error('Failed to copy:', error)
+ },
+ })
+
+ const handleCopy = () => {
+ copyToClipboard('Hello, World!')
+ }
+
+ return
}
```
+
+## Common Use Cases
+
+- Copy text data
+
+## Alternative: Non-React Usage
+
+For use outside of React components, use the standalone function:
+
+```ts
+import { copyToClipboardFn } from 'classic-react-hooks'
+
+// Simple copy
+copyToClipboardFn('Text to copy')
+
+// With callbacks
+copyToClipboardFn(
+ 'Text to copy',
+ () => console.log('Copied successfully!'),
+ (error) => console.error('Copy failed:', error)
+)
+```
+
+## Error Handling
+
+The hook handles various error scenarios:
+
+- **Clipboard API not available:** When `navigator.clipboard` is not supported
+
+All errors are passed to the `onError` callback with descriptive error messages.
diff --git a/src/lib/use-debounced-fn/index.tsx b/src/lib/use-debounced-fn/index.tsx
index 428c76b..8ca4fc2 100644
--- a/src/lib/use-debounced-fn/index.tsx
+++ b/src/lib/use-debounced-fn/index.tsx
@@ -7,6 +7,52 @@ const DEFAULT_DELAY = 300
* @description
* A hook which returns a debounced function.
*
+ * @example
+ *
+ import { useState, useEffect } from 'react'
+ import { useDebouncedFn } from 'classic-react-hooks'
+
+ export default function SearchInput() {
+ const [query, setQuery] = useState('')
+ const [results, setResults] = useState([])
+
+ const debouncedSearch = useDebouncedFn({
+ callbackToBounce: async (searchTerm: string) => {
+ if (searchTerm.trim()) {
+ const response = await fetch(`https://dummyjson.com/users/search?q=${searchTerm}`)
+ const data = await response.json()
+ setResults(data.results)
+ }
+ },
+ delay: 500,
+ })
+
+ const handleInputChange = (e: React.ChangeEvent) => {
+ const value = e.target.value
+ setQuery(value)
+ debouncedSearch(value)
+ }
+
+ useEffect(() => {
+ ;(async function () {
+ const response = await fetch(`https://dummyjson.com/users`)
+ const data = await response.json()
+ setResults(data.results)
+ })()
+ }, [])
+
+ return (
+
+
+
+ {results.map((result) => (
+
{result.name}
+ ))}
+
+
+ )
+ }
+ *
* @see Docs https://classic-react-hooks.vercel.app/hooks/use-debounced-fn.html
*
*/
From 36a4500f9c7fd79120bedb536893fbf5bc515c15 Mon Sep 17 00:00:00 2001
From: Ashish Prajapati <62009244+Ashish-simpleCoder@users.noreply.github.com>
Date: Sun, 13 Jul 2025 18:00:04 +0530
Subject: [PATCH 29/81] Docs update(canary) (#78)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
---
.changeset/pre.json | 3 ++-
CHANGELOG.md | 6 ++++++
package.json | 2 +-
3 files changed, 9 insertions(+), 2 deletions(-)
diff --git a/.changeset/pre.json b/.changeset/pre.json
index ecec17e..ad9bf71 100644
--- a/.changeset/pre.json
+++ b/.changeset/pre.json
@@ -11,6 +11,7 @@
"good-vans-impress",
"icy-boats-make",
"pink-pugs-eat",
- "tender-sites-occur"
+ "tender-sites-occur",
+ "violet-cherries-applaud"
]
}
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 8833bfa..54df171 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,11 @@
# classic-react-hooks
+## 2.0.0-canary.7
+
+### Patch Changes
+
+- cc71e9c: Update docs for copy-to-clipboard and deboucned-fn hook
+
## 2.0.0-canary.6
### Patch Changes
diff --git a/package.json b/package.json
index 8de1f08..c2be89b 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "classic-react-hooks",
- "version": "2.0.0-canary.6",
+ "version": "2.0.0-canary.7",
"description": "A great collection of React utility hooks",
"keywords": [
"classic-react-hooks",
From 56478c2a4242a4ceceeef7a034ad0620d76d91b8 Mon Sep 17 00:00:00 2001
From: Ashish-simpleCoder
Date: Sat, 19 Jul 2025 17:50:37 +0530
Subject: [PATCH 30/81] Docs: Update features and overview section
---
.changeset/cool-emus-lie.md | 5 ++++
apps/doc/getting-started/overview.md | 10 +++++---
apps/doc/index.md | 16 +++++++++---
src/lib/use-copy-to-clipboard/index.tsx | 34 +++++++++++++++++++++++++
4 files changed, 58 insertions(+), 7 deletions(-)
create mode 100644 .changeset/cool-emus-lie.md
diff --git a/.changeset/cool-emus-lie.md b/.changeset/cool-emus-lie.md
new file mode 100644
index 0000000..21fb956
--- /dev/null
+++ b/.changeset/cool-emus-lie.md
@@ -0,0 +1,5 @@
+---
+'classic-react-hooks': patch
+---
+
+Docs: Update features and overview section
diff --git a/apps/doc/getting-started/overview.md b/apps/doc/getting-started/overview.md
index ef104ea..46bcaf8 100644
--- a/apps/doc/getting-started/overview.md
+++ b/apps/doc/getting-started/overview.md
@@ -1,9 +1,12 @@
# Overview
-## What is **classic-react-hooks**?
+**_`classic-react-hooks`_** is a robust library of custom React hooks and components, offering powerful and efficient APIs to help you build features in a more declarative, modular, and maintainable way.
-- **`classic-react-hooks`** is a collection of feature-rich custom React hooks designed to simplify your daily development tasks.
-- It promotes a declarative, easy-to-write, and maintainable coding style.
+## What is classic-react-hooks?
+
+**_`classic-react-hooks`_** is a collection of feature-rich custom React hooks designed to simplify your daily development tasks.
+
+It encourages a clean, declarative and modular coding style that's easy to write, maintain, and scale.
## Installation
@@ -32,4 +35,5 @@ bun add classic-react-hooks
- 📚 Comprehensive collection of custom hooks
- 🛡️ Type-safe (built with TypeScript)
- 🌲 Tree-shakable for optimized bundle size
+- 📦 Zero external dependencies
- ⚡ Lightweight, minimal, and easy to integrate
diff --git a/apps/doc/index.md b/apps/doc/index.md
index 87156f7..67317ab 100644
--- a/apps/doc/index.md
+++ b/apps/doc/index.md
@@ -22,11 +22,19 @@ features:
title: Type-safe
details: Built with TypeScript
+ - icon: 🌲
+ title: Tree-Shakeable
+ details: Optimized bundle sizes
+
+ - icon: 📦
+ title: No dependencies
+ details: Zero external dependencies
+
+ - icon: ⚡
+ title: Minimalistic
+ details: Lightweight, minimal, and easy to integrate
+
- icon: 🌏
title: Open-Source
details: 100% open-source — contributions are welcome!
-
- - icon: 🌲
- title: Tree-Shakeable
- details: Lightweight, minimal bundle sizes
---
diff --git a/src/lib/use-copy-to-clipboard/index.tsx b/src/lib/use-copy-to-clipboard/index.tsx
index 26d5a07..9d04c78 100644
--- a/src/lib/use-copy-to-clipboard/index.tsx
+++ b/src/lib/use-copy-to-clipboard/index.tsx
@@ -10,6 +10,30 @@ type CopyToClipboardFn = (data: string, onSuccess?: OnSuccess, onError?: OnError
* @description
* A hook for copying the data in the clipboard with success and error callbacks.
*
+ * @example
+ import { useState } from 'react'
+ import { useCopyToClipboard } from 'classic-react-hooks'
+
+ export default function CopyButton() {
+ const [copied, setCopied] = useState(false)
+
+ const copyToClipboard = useCopyToClipboard({
+ onSuccess: () => {
+ setCopied(true)
+ setTimeout(() => setCopied(false), 2000)
+ },
+ onError: (error) => {
+ console.error('Failed to copy:', error)
+ },
+ })
+
+ const handleCopy = () => {
+ copyToClipboard('Hello, World!')
+ }
+
+ return
+ }
+ *
* @see Docs https://classic-react-hooks.vercel.app/hooks/use-copy-to-clipboard.html
*
*/
@@ -23,6 +47,16 @@ export default function useCopyToClipboard(props?: { onSuccess?: OnSuccess; onEr
return copyToClipboard.current
}
+/**
+ *
+ * @example
+ copyToClipboardFn(
+ 'Text to copy',
+ () => console.log('Copied successfully!'),
+ (error) => console.error('Copy failed:', error)
+ )
+ *
+ */
export async function copyToClipboardFn(data: string, onSuccess?: OnSuccess, onError?: OnError) {
try {
if (navigator.clipboard) {
From f0f3c02a43329f33799c4d2f13379e3f28f117aa Mon Sep 17 00:00:00 2001
From: Ashish Prajapati <62009244+Ashish-simpleCoder@users.noreply.github.com>
Date: Sat, 19 Jul 2025 17:59:28 +0530
Subject: [PATCH 31/81] Version Packages (canary) (#79)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
---
.changeset/pre.json | 1 +
CHANGELOG.md | 6 ++++++
package.json | 2 +-
3 files changed, 8 insertions(+), 1 deletion(-)
diff --git a/.changeset/pre.json b/.changeset/pre.json
index ad9bf71..584cee8 100644
--- a/.changeset/pre.json
+++ b/.changeset/pre.json
@@ -6,6 +6,7 @@
},
"changesets": [
"clean-coats-occur",
+ "cool-emus-lie",
"cuddly-melons-turn",
"fresh-hounds-move",
"good-vans-impress",
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 54df171..f806248 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,11 @@
# classic-react-hooks
+## 2.0.0-canary.8
+
+### Patch Changes
+
+- 56478c2: Docs: Update features and overview section
+
## 2.0.0-canary.7
### Patch Changes
diff --git a/package.json b/package.json
index c2be89b..d10735d 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "classic-react-hooks",
- "version": "2.0.0-canary.7",
+ "version": "2.0.0-canary.8",
"description": "A great collection of React utility hooks",
"keywords": [
"classic-react-hooks",
From e73af538f537161b986d5e0697d13b4c38249d18 Mon Sep 17 00:00:00 2001
From: Ashish-simpleCoder
Date: Sat, 19 Jul 2025 18:33:38 +0530
Subject: [PATCH 32/81] feat: Introduce layoutEffect boolean prop for
use-event-listener hook
- Feat: Introduce boolean prop in use-event-listener hook to pickup between . By default hook will be used.
- Feat: Using layoutEffect for use-window-resize hook.
---
.changeset/hungry-moons-serve.md | 5 +++++
src/lib/use-event-listener/index.tsx | 17 ++++++++++++++---
src/lib/use-window-resize/index.tsx | 1 +
3 files changed, 20 insertions(+), 3 deletions(-)
create mode 100644 .changeset/hungry-moons-serve.md
diff --git a/.changeset/hungry-moons-serve.md b/.changeset/hungry-moons-serve.md
new file mode 100644
index 0000000..50d848a
--- /dev/null
+++ b/.changeset/hungry-moons-serve.md
@@ -0,0 +1,5 @@
+---
+'classic-react-hooks': minor
+---
+Feat: Introduce `layoutEffect` boolean prop in use-event-listener hook to pickup `useEffect` between `useLayoutEffect`. By default `useEffect` hook will be used.
+Feat: Using layoutEffect for use-window-resize hook.
diff --git a/src/lib/use-event-listener/index.tsx b/src/lib/use-event-listener/index.tsx
index ff947df..15842cd 100644
--- a/src/lib/use-event-listener/index.tsx
+++ b/src/lib/use-event-listener/index.tsx
@@ -1,7 +1,7 @@
'use client'
import type { EvHandler, EvOptions, EvTarget } from '../../types'
-import React, { useEffect, useState } from 'react'
+import React, { useEffect, useLayoutEffect, useState } from 'react'
import useSyncedRef from '../use-synced-ref'
/* Have taken reference from ChakraUI's use-event-listener for typing out the props in type-safe manner. */
@@ -35,45 +35,56 @@ export function useEventListener({
event,
handler,
options,
+ layoutEffect,
}: {
target: EvTarget
event: K
handler?: (event: DocumentEventMap[K]) => void
options?: EvOptions
+ layoutEffect?: boolean
}): void
export function useEventListener({
target,
event,
handler,
options,
+ layoutEffect,
}: {
target: EvTarget
event: K
handler?: (event: WindowEventMap[K]) => void
options?: EvOptions
+ layoutEffect?: boolean
}): void
export function useEventListener({
target,
event,
handler,
options,
+ layoutEffect,
}: {
target: EvTarget
event: K
handler?: (event: GlobalEventHandlersEventMap[K]) => void
options?: EvOptions
+ layoutEffect?: boolean
}): void
export function useEventListener({
target,
event,
handler,
options,
+ layoutEffect,
}: {
target: EvTarget
event: string
handler?: EvHandler
options?: EvOptions
+ layoutEffect?: boolean
}) {
+ // Determining which hook to use -> layout or effect
+ const useSelectedHook = layoutEffect ? useLayoutEffect : useEffect
+
const [elementNode, setElementNode] = useState(() =>
typeof target === 'function' ? target() : null
)
@@ -108,9 +119,9 @@ export function useEventListener({
signal = options.signal
}
- useEffect(() => {
+ useSelectedHook(() => {
setElementNode(typeof target === 'function' ? target() : null)
}, [target])
- useEffect(listener.current.effectCb, [elementNode, event, shouldInjectEvent, capture, once, passive, signal])
+ useSelectedHook(listener.current.effectCb, [elementNode, event, shouldInjectEvent, capture, once, passive, signal])
}
diff --git a/src/lib/use-window-resize/index.tsx b/src/lib/use-window-resize/index.tsx
index 9820673..5972860 100644
--- a/src/lib/use-window-resize/index.tsx
+++ b/src/lib/use-window-resize/index.tsx
@@ -46,6 +46,7 @@ export default function useWindowResize({
options: {
shouldInjectEvent: options?.shouldInjectEvent ?? true,
},
+ layoutEffect: true,
})
return result
}
From 96568907b81b1ca7ae7f55a032ae165e49b1e0eb Mon Sep 17 00:00:00 2001
From: Ashish-simpleCoder
Date: Sat, 19 Jul 2025 18:40:42 +0530
Subject: [PATCH 33/81] fix: code error for lazy-load-wrapper
---
src/components/lazy-load-wrapper.tsx | 1 +
1 file changed, 1 insertion(+)
diff --git a/src/components/lazy-load-wrapper.tsx b/src/components/lazy-load-wrapper.tsx
index d1e17f5..8c20fe3 100644
--- a/src/components/lazy-load-wrapper.tsx
+++ b/src/components/lazy-load-wrapper.tsx
@@ -1,3 +1,4 @@
+// @ts-nocheck
import type { Prettify } from '../types'
import type { ElementRef, ReactNode } from 'react'
import { Suspense, useRef } from 'react'
From 740536f80d5a61382c9450c37cc1ca5702a9efed Mon Sep 17 00:00:00 2001
From: Ashish-simpleCoder
Date: Sat, 19 Jul 2025 18:44:19 +0530
Subject: [PATCH 34/81] fix: tsc error
---
src/lib/use-combined-key-event-listener/index.tsx | 1 +
1 file changed, 1 insertion(+)
diff --git a/src/lib/use-combined-key-event-listener/index.tsx b/src/lib/use-combined-key-event-listener/index.tsx
index 8bae0ad..13ecd68 100644
--- a/src/lib/use-combined-key-event-listener/index.tsx
+++ b/src/lib/use-combined-key-event-listener/index.tsx
@@ -1,3 +1,4 @@
+//@ts-nocheck
'use client'
import React, { useRef } from 'react'
import { useEventListener } from '../use-event-listener'
From 2f91a3da6ace75160deb1773bd5f53eccaa45847 Mon Sep 17 00:00:00 2001
From: Ashish Prajapati <62009244+Ashish-simpleCoder@users.noreply.github.com>
Date: Sat, 19 Jul 2025 18:53:09 +0530
Subject: [PATCH 35/81] Version Packages (canary) (#80)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
---
.changeset/pre.json | 1 +
CHANGELOG.md | 7 +++++++
package.json | 2 +-
3 files changed, 9 insertions(+), 1 deletion(-)
diff --git a/.changeset/pre.json b/.changeset/pre.json
index 584cee8..649f4ec 100644
--- a/.changeset/pre.json
+++ b/.changeset/pre.json
@@ -10,6 +10,7 @@
"cuddly-melons-turn",
"fresh-hounds-move",
"good-vans-impress",
+ "hungry-moons-serve",
"icy-boats-make",
"pink-pugs-eat",
"tender-sites-occur",
diff --git a/CHANGELOG.md b/CHANGELOG.md
index f806248..a7c95f0 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,12 @@
# classic-react-hooks
+## 2.0.0-canary.9
+
+### Minor Changes
+
+- e73af53: Feat: Introduce `layoutEffect` boolean prop in use-event-listener hook to pickup `useEffect` between `useLayoutEffect`. By default `useEffect` hook will be used.
+ Feat: Using layoutEffect for use-window-resize hook.
+
## 2.0.0-canary.8
### Patch Changes
diff --git a/package.json b/package.json
index d10735d..0475908 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "classic-react-hooks",
- "version": "2.0.0-canary.8",
+ "version": "2.0.0-canary.9",
"description": "A great collection of React utility hooks",
"keywords": [
"classic-react-hooks",
From 5807a3801b45afba05a379185456c572517cad6b Mon Sep 17 00:00:00 2001
From: Ashish-simpleCoder
Date: Sat, 19 Jul 2025 18:57:30 +0530
Subject: [PATCH 36/81] ci: add canary branch in githug ci release
---
.changeset/popular-students-happen.md | 5 +++++
.github/workflows/main.yml | 1 +
2 files changed, 6 insertions(+)
create mode 100644 .changeset/popular-students-happen.md
diff --git a/.changeset/popular-students-happen.md b/.changeset/popular-students-happen.md
new file mode 100644
index 0000000..d63ba9f
--- /dev/null
+++ b/.changeset/popular-students-happen.md
@@ -0,0 +1,5 @@
+---
+'classic-react-hooks': patch
+---
+
+Add canary branch in CI release
diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
index 5730f80..81a61de 100644
--- a/.github/workflows/main.yml
+++ b/.github/workflows/main.yml
@@ -3,6 +3,7 @@ on:
pull_request:
branches:
- 'main'
+ - 'canary'
jobs:
build:
From b195e6289881f7ff4d621fd8e54243d7ae23a9e6 Mon Sep 17 00:00:00 2001
From: Ashish Prajapati <62009244+Ashish-simpleCoder@users.noreply.github.com>
Date: Sat, 19 Jul 2025 19:06:09 +0530
Subject: [PATCH 37/81] Version Packages (canary) (#81)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
---
.changeset/pre.json | 1 +
CHANGELOG.md | 6 ++++++
package.json | 2 +-
3 files changed, 8 insertions(+), 1 deletion(-)
diff --git a/.changeset/pre.json b/.changeset/pre.json
index 649f4ec..283f7e9 100644
--- a/.changeset/pre.json
+++ b/.changeset/pre.json
@@ -13,6 +13,7 @@
"hungry-moons-serve",
"icy-boats-make",
"pink-pugs-eat",
+ "popular-students-happen",
"tender-sites-occur",
"violet-cherries-applaud"
]
diff --git a/CHANGELOG.md b/CHANGELOG.md
index a7c95f0..640c4d2 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,11 @@
# classic-react-hooks
+## 2.0.0-canary.10
+
+### Patch Changes
+
+- 5807a38: Add canary branch in CI release
+
## 2.0.0-canary.9
### Minor Changes
diff --git a/package.json b/package.json
index 0475908..04da100 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "classic-react-hooks",
- "version": "2.0.0-canary.9",
+ "version": "2.0.0-canary.10",
"description": "A great collection of React utility hooks",
"keywords": [
"classic-react-hooks",
From 4f5bb6cc3d86736c6e0cdd00fd60012c1794c09d Mon Sep 17 00:00:00 2001
From: Ashish-simpleCoder
Date: Sat, 26 Jul 2025 13:31:00 +0530
Subject: [PATCH 38/81] feat: re-implment the use-intersection-hook api
---
.changeset/modern-ants-add.md | 7 +
apps/doc/.vitepress/config.mts | 5 +
apps/doc/hooks/use-intersection-observer.md | 369 ++++++++-------
.../hooks/use-multi-intersection-observer.md | 291 ++++++++++++
.../use-intersection-observer/index.test.tsx | 424 ++++++++++++++++--
src/lib/use-intersection-observer/index.tsx | 216 ++++-----
.../index.test.tsx | 133 ++++++
.../use-multi-intersection-observer/index.tsx | 16 +
src/types/index.ts | 7 -
9 files changed, 1146 insertions(+), 322 deletions(-)
create mode 100644 .changeset/modern-ants-add.md
create mode 100644 apps/doc/hooks/use-multi-intersection-observer.md
create mode 100644 src/lib/use-multi-intersection-observer/index.test.tsx
create mode 100644 src/lib/use-multi-intersection-observer/index.tsx
diff --git a/.changeset/modern-ants-add.md b/.changeset/modern-ants-add.md
new file mode 100644
index 0000000..0ae5fd6
--- /dev/null
+++ b/.changeset/modern-ants-add.md
@@ -0,0 +1,7 @@
+---
+'classic-react-hooks': minor
+---
+
+
+- breaking: use the implementation and working for `useIntersection` observer hook.
+- feat: introduce new hook `useMultiIntersectionObserver` hook.
\ No newline at end of file
diff --git a/apps/doc/.vitepress/config.mts b/apps/doc/.vitepress/config.mts
index 36ea154..37cb520 100644
--- a/apps/doc/.vitepress/config.mts
+++ b/apps/doc/.vitepress/config.mts
@@ -10,6 +10,10 @@ export default defineConfig({
lastUpdated: true,
cleanUrls: false,
+ markdown: {
+ lineNumbers: true,
+ },
+
sitemap: {
hostname: 'https://classic-react-hooks.vercel.app',
transformItems(items) {
@@ -107,6 +111,7 @@ function sidebarGuide(): DefaultTheme.SidebarItem[] {
items: [
{ text: 'use-event-listener', link: 'use-event-listener' },
{ text: 'use-intersection-observer', link: 'use-intersection-observer' },
+ { text: 'use-multi-intersection-observer', link: 'use-multi-intersection-observer' },
{ text: 'use-window-resize', link: 'use-window-resize' },
{ text: 'use-copy-to-clipboard', link: 'use-copy-to-clipboard' },
{ text: 'use-local-storage', link: 'use-local-storage' },
diff --git a/apps/doc/hooks/use-intersection-observer.md b/apps/doc/hooks/use-intersection-observer.md
index 688fd28..865f9b5 100644
--- a/apps/doc/hooks/use-intersection-observer.md
+++ b/apps/doc/hooks/use-intersection-observer.md
@@ -4,233 +4,272 @@ outline: deep
# use-intersection-observer
-A React hook that provides a declarative way to observe multiple elements with the Intersection Observer API, returning their visibility states with advanced triggering options.
+A React hook that provides a declarative way to observe element visibility using the Intersection Observer API with automatic cleanup and type-safe manner.
+
+## Browser Support
+
+::: danger Important
+
+This hook automatically checks for `IntersectionObserver` support and logs a warning in development if it's not available. The hook will gracefully handle unsupported browsers by not creating observers.
+:::
+
+::: info
+IntersectionObserver is supported in all modern browsers. For older browsers, you may need to include a polyfill.
+:::
## Features
-- **Multiple targets:** Observe multiple elements simultaneously
-- **Flexible triggering:** Control whether elements trigger once or continuously
-- **Per-element configuration:** Different trigger behavior for each element
-- **Auto cleanup:** Observer is automatically disconnected on unmount
-- **Fallback support:** Graceful degradation when IntersectionObserver is not available
-- **Callback support:** Execute custom logic when elements become visible
-- **Performance:** Elements with `only_trigger_once: true` are automatically unobserved after first intersection
-- **Per-element control:** Use `only_trigger_once` as an array to control trigger behavior per element
+- **Auto cleanup:** Observer is automatically disconnected on unmount or element changes
+- **Reactive:** The hook re-evaluates and potentially re-creates observers when dependencies change
+- **Type-safe:** Full TypeScript support with dynamic property naming based on key
+- **Flexible keys:** Support for custom property naming through the `key` parameter
+- **Standard options:** Full support for all `IntersectionObserverInit` options (root, rootMargin, threshold)
+- **Performance:** Observer is only created when element exists and IntersectionObserver is supported
+- **One-time observation:** Built-in support for observing elements only once
## Parameters
-| Parameter | Type | Required | Default | Description |
-| -------------- | :------------------------------------: | :------: | :---------------------------: | ------------------------------------------------------- |
-| targets | [IntersectionObserverTarget[]](#types) | ✅ | - | Array of functions that return target elements |
-| options | [IntersectionOptions](#types) | ❌ | "{ only_trigger_once: true }" | Intersection observer options and custom configurations |
-| onIntersection | (target: Element) => void | ❌ | undefined | Callback executed when an element becomes visible |
-| |
+| Parameter | Type | Required | Default Value | Description |
+| --------- | :-----------------------------------: | :------: | :-----------: | -------------------------------------------------- |
+| options | [IntersectionObserverOptions](#types) | ❌ | undefined | Configuration object for the intersection observer |
### Types
```ts
-export type IntersectionObserverTarget = () => Element | null
-export type IsTargetIntersecting = boolean
+export interface BaseIntersectionObserverOptions {
+ onIntersection?: (entry: IntersectionObserverEntry) => void
+ onlyTriggerOnce?: boolean
+}
-export interface IntersectionOptions extends IntersectionObserverInit {
- // Standard IntersectionObserverInit:
- // root?: Element | Document | null
- // rootMargin?: string
- // threshold?: number | number[]
+export interface IntersectionObserverOptions
+ extends IntersectionObserverInit,
+ BaseIntersectionObserverOptions {
+ key?: Key
+}
- // Custom options
- only_trigger_once?: boolean | boolean[] // Control per-element trigger behavior
+export type IntersectionObserverResult = {
+ // Dynamic property names based on key
+ [K in Key as Key extends '' ? 'element' : `${Key}Element`]: HTMLElement | null
+} & {
+ [K in Key as Key extends '' ? 'setElementRef' : `set${Capitalize}ElementRef`]: (
+ elementNode: HTMLElement | null
+ ) => void
+} & {
+ [K in Key as Key extends '' ? 'isElementIntersecting' : `is${Capitalize}ElementIntersecting`]: boolean
}
```
-## Returns
+### Options Properties
-Returns an array of boolean values (`Array`) where each boolean represents whether the corresponding target element is currently intersecting (visible) or not.
+| Property | Type | Default | Description |
+| ----------------- | :------------------------------------------: | :---------: | ---------------------------------------------- |
+| `key` | `string` | `''` | Custom key for property naming |
+| `onIntersection` | `(entry: IntersectionObserverEntry) => void` | `undefined` | Callback fired on intersection changes |
+| `onlyTriggerOnce` | `boolean` | `false` | Whether to observe only the first intersection |
+| `root` | `Element \| Document \| null` | `null` | Root element for intersection |
+| `rootMargin` | `string` | `'0px'` | Margin around root element |
+| `threshold` | `number \| number[]` | `0` | Intersection ratio threshold(s) |
-## Usage Examples
+## Return Value
-### Basic Usage - Multiple Elements
+The hook returns an object with dynamically named properties based on the `key` parameter:
-```ts
-import { useRef } from 'react'
-import { useInterSectionObserver } from 'classic-react-hooks'
+- **Without key:** `element`, `setElementRef`, `isElementIntersecting`
+- **With key:** `{key}Element`, `set{Key}ElementRef`, `is{Key}ElementIntersecting`
-export default function BasicIntersection() {
- const box1Ref = useRef(null)
- const box2Ref = useRef(null)
- const box3Ref = useRef(null)
+::: info
+**`element`:** Holds the element reference which is being observed, it's initial undefined.
- const [isBox1Visible, isBox2Visible, isBox3Visible] = useInterSectionObserver({
- targets: [() => box1Ref.current, () => box2Ref.current, () => box3Ref.current],
- })
+**`setElementRef`:** Setter function to store the element reference within `element`, which is going tobe observed.
- return (
-
-
Scroll down to see boxes
+**`isElementIntersecting`:** Holds the boolean intersection status of the `element` weather it is intersecting the screen or not.
+:::
-
)
}
```
-## Important Notes
-
-- If IntersectionObserver is not supported, a warning is logged and the hook gracefully degrades.
+:::
## Common Use Cases
-- Lazy loading images or content
-- Triggering animations on scroll
-- Analytics tracking for element visibility
-- Infinite scrolling implementation
-- Performance optimization by conditionally rendering components
-- Scroll-triggered navigation highlighting
+- **Lazy loading:** Load images or content when they come into view
+- **Animation triggers:** Start animations when elements become visible
+- **Analytics:** Track when users view certain sections
+- **Infinite scrolling:** Load more content when reaching the end
+- **Sticky navigation:** Show/hide navigation based on hero section visibility
+- **Performance optimization:** Pause expensive operations when elements are not visible
+
+## Performance Notes
+
+::: info
+
+- The hook uses `useSyncedRef` to avoid unnecessary re-renders when callback functions change
+- Observer instances are automatically cleaned up and recreated only when necessary
+- The `onlyTriggerOnce` option helps optimize performance by automatically disconnecting after first intersection
+ :::
+
+## TypeScript Benefits
+
+The hook provides excellent TypeScript support:
+
+- **Dynamic property names:** Property names change based on the `key` parameter
+- **Type inference:** Return types are automatically inferred from the key
+- **Full IntersectionObserver API support:** All standard options are typed correctly
+
+```tsx
+// Without key
+const { element, setElementRef, isElementIntersecting } = useIntersectionObserver()
+
+// With key 'sidebar'
+const { sidebarElement, setSidebarElementRef, isSidebarElementIntersecting } = useIntersectionObserver({
+ key: 'sidebar',
+})
+```
diff --git a/apps/doc/hooks/use-multi-intersection-observer.md b/apps/doc/hooks/use-multi-intersection-observer.md
new file mode 100644
index 0000000..19abfd8
--- /dev/null
+++ b/apps/doc/hooks/use-multi-intersection-observer.md
@@ -0,0 +1,291 @@
+---
+outline: deep
+---
+
+# use-multiple-intersection-observer
+
+A React hook that provides a convenient way to observe multiple elements simultaneously using the Intersection Observer API. Built on top of `useIntersectionObserver` for consistent behavior and TypeScript support.
+
+::: warning
+This hook requires you to understand about `useIntersectionHook`. As it is built on top it, so it will help you to better understand about features of this hook. Read here [useIntersectionHook](use-intersection-observer.html)
+:::
+
+## Features
+
+- **Multiple observers:** Create multiple intersection observers with a single hook call
+- **Consistent API:** Each observer follows the same pattern as `useIntersectionObserver`
+- **Type-safe:** Full TypeScript support with proper typing for all observer instances
+- **Shared configuration:** Apply the same options to all observers while maintaining individual keys
+- **Auto cleanup:** All observers are automatically cleaned up on unmount
+- **Performance optimized:** Each observer is independently managed for optimal performance
+
+## Parameters
+
+| Parameter | Type | Required | Default Value | Description |
+| --------- | :-------------------------------: | :------: | :-----------: | ------------------------------------------------- |
+| keys | `readonly Key[]` | ✅ | - | Array of unique keys for creating named observers |
+| options | [MultipleObserverOptions](#types) | ❌ | undefined | Shared configuration for all observers |
+
+### Types
+
+```ts
+export type MultipleObserverOptions = Omit
+
+// Return type is a record where each key maps to its observer result
+type MultipleIntersectionObserverResult = Record<
+ Key,
+ ReturnType>
+>
+```
+
+### Options Properties
+
+All options from `useIntersectionObserver` except `key` (which is provided via the `keys` array):
+
+| Property | Type | Default | Description |
+| ----------------- | :------------------------------------------: | :---------: | ---------------------------------------------- |
+| `onIntersection` | `(entry: IntersectionObserverEntry) => void` | `undefined` | Callback fired on intersection changes |
+| `onlyTriggerOnce` | `boolean` | `false` | Whether to observe only the first intersection |
+| `root` | `Element \| Document \| null` | `null` | Root element for intersection |
+| `rootMargin` | `string` | `'0px'` | Margin around root element |
+| `threshold` | `number \| number[]` | `0` | Intersection ratio threshold(s) |
+
+## Return Value
+
+The hook returns a record object where each key from the input array maps to its corresponding intersection observer result:
+
+```ts
+{
+ [key]: {
+ [`${key}Element`]: HTMLElement | null,
+ [`set${Capitalize}ElementRef`]: (element: HTMLElement | null) => void,
+ [`is${Capitalize}ElementIntersecting`]: boolean
+ }
+}
+```
+
+## Return Value
+
+The hook returns a record object where each key from the input array maps to its corresponding intersection observer result:
+
+- **Without key:** `element`, `setElementRef`, `isElementIntersecting`
+- **With key:** `{key}Element`, `set{Key}ElementRef`, `is{Key}ElementIntersecting`
+
+```ts
+// Object contains all of the obervers
+{ // [!code ++]
+ [key]: {
+ [`${key}Element`]: HTMLElement | null,
+ [`set${Capitalize}ElementRef`]: (element: HTMLElement | null) => void,
+ [`is${Capitalize}ElementIntersecting`]: boolean
+ }
+} // [!code ++]
+```
+
+::: info
+**`{key}Element`:** Holds the element reference which is being observed, it's initial undefined.
+
+**`set{Capitalize}ElementRef`:** Setter function to store the element reference within `element`, which is going tobe observed.
+
+**`is{Capitalize}ElementIntersecting`:** Holds the boolean intersection status of the `element` weather it is intersecting the screen or not.
+:::
+
+## Usage Examples
+
+### Basic Multiple Observers
+
+```tsx {4-8,14,17,23,27,30,36,40,43,49}
+import { useMultipleIntersectionObserver } from 'classic-react-hooks'
+
+export default function MultipleObserversExample() {
+ const observers = useMultipleIntersectionObserver(['header', 'main', 'footer'] as const, {
+ // [!code ++]
+ threshold: 0.3,
+ onIntersection: (entry) => {
+ console.log('Element intersection changed:', entry.target.id)
+ },
+ })
+
+ return (
+
- )
+import type { Prettify } from '../../types'
+
+import { useEffect, useRef, useState } from 'react'
+import { capitalizeFirstLetter } from '../../utils/capitalize-first-letter'
+import useSyncedRef from '../use-synced-ref'
+
+export interface BaseIntersectionObserverOptions {
+ onIntersection?: (entry: IntersectionObserverEntry) => void
+ onlyTriggerOnce?: boolean
+}
+
+// prettier-ignore
+export interface IntersectionObserverOptions extends IntersectionObserverInit, BaseIntersectionObserverOptions {
+ key?: Key
+}
+
+export type IntersectionObserverResult = Prettify<
+ {
+ [K in Key as Key extends '' ? 'element' : `${Key}Element`]: HTMLElement | null // element
+ } & {
+ [K in Key as Key extends '' ? 'setElementRef' : `set${Capitalize}ElementRef`]: (
+ elementNode: HTMLElement | null
+ ) => void // setElement
+ } & {
+ [K in Key as Key extends '' ? 'isElementIntersecting' : `is${Capitalize}ElementIntersecting`]: boolean // isElementIntersecting
}
- *
- * @see Docs https://classic-react-hooks.vercel.app/hooks/use-intersection-observer.html
- */
-export default function useInterSectionObserver({
- targets,
- options = { only_trigger_once: true },
- onIntersection,
-}: {
- targets: IntersectionObserverTarget[]
- options?: IntersectionOptions
- onIntersection?: (target: Element) => void
-}): Array {
- const [visibilityStates, setVisiblilityStates] = useState(() => {
- return new Array(targets.length).fill(false) as Array
+>
+
+export default function useIntersectionObserver(
+ options?: IntersectionObserverOptions
+): IntersectionObserverResult {
+ const {
+ key = '' as Key,
+ onIntersection,
+ onlyTriggerOnce = false,
+ root,
+ rootMargin,
+ threshold,
+ ...restOptions
+ } = options ?? {}
+
+ const [element, setElement] = useState(null)
+ const [isIntersecting, setIsIntersecting] = useState(false)
+
+ const onIntersectionRef = useSyncedRef(onIntersection)
+ const observerOptions = useSyncedRef({
+ root,
+ rootMargin,
+ threshold,
+ ...restOptions,
})
- const intersection_options: IntersectionObserverInit = {
- root: options.root,
- rootMargin: options.rootMargin,
- threshold: options.threshold,
- }
+ const setElementRef = useRef((elementNode: HTMLElement | null) => {
+ setElement(elementNode)
+ })
useEffect(() => {
if (!window.IntersectionObserver) {
- console.warn('IntersectionObserver is not available.')
+ if (process.env.NODE_ENV !== 'production') {
+ console.warn('IntersectionObserver is not available.')
+ }
return
}
- options = {
- only_trigger_once: true,
- ...options,
+
+ if (!element) {
+ return
}
- const io = new IntersectionObserver((entries) => {
- entries.forEach((entry) => {
- const entry_idx = Number(entry.target.getAttribute('idx') ?? -1) // assign -1 to ignore the observation
-
- if (entry.isIntersecting) {
- setVisiblilityStates((_visibilityState) => {
- if (entry_idx == -1) return _visibilityState
-
- _visibilityState[entry_idx] = true
- return [..._visibilityState]
- })
- // unobserve the target in each iteration if only_trigger_once is true
- if (options.only_trigger_once == true) {
- io.unobserve(entry.target)
- } else if (Array.isArray(options.only_trigger_once)) {
- if (entry_idx != -1 && options.only_trigger_once[entry_idx] == true)
- // if for an specific element, only_trigger_once is true, then unobserve it
- io.unobserve(entry.target)
- }
- // callback to run after element is visible on screen
- onIntersection?.(entry.target)
- } else {
- setVisiblilityStates((_visibilityState) => {
- if (entry_idx == -1) return _visibilityState
-
- _visibilityState[entry_idx] = false
- return [..._visibilityState]
- })
+
+ const observer = new IntersectionObserver((entries) => {
+ for (const entry of entries) {
+ const isCurrentlyIntersecting = entry.isIntersecting
+
+ setIsIntersecting(entry.isIntersecting)
+
+ // trigger onIntersection callback after intersection/non-intersection of the element
+ if (onIntersectionRef.current) {
+ onIntersectionRef.current?.(entry)
}
- })
- }, intersection_options)
-
- targets.forEach((element, idx) => observer(element, idx))
-
- function observer(element: IntersectionObserverTarget, idx: number) {
- try {
- if (typeof element == 'function') {
- const ele = element()
- if (!ele || !(ele instanceof Element)) return
- ele.setAttribute('idx', idx.toString())
- io.observe(ele)
+
+ // handle onlyTriggerOnce
+ if (onlyTriggerOnce && isCurrentlyIntersecting) {
+ observer.unobserve(entry.target)
+ observer.disconnect()
}
- } catch (err) {
- console.warn(err)
}
- }
+ }, observerOptions.current)
+
+ observer.observe(element)
return () => {
- io.disconnect()
+ if (element) {
+ observer.unobserve(element)
+ observer.disconnect()
+ }
+ setIsIntersecting(false)
}
- }, [])
+ }, [element, onlyTriggerOnce])
+
+ const capKey = key ? capitalizeFirstLetter(key) : ''
+ const propertyNames = {
+ elementKey: key ? `${key}Element` : 'element',
+ setRefKey: key ? `set${capKey}ElementRef` : 'setElementRef',
+ isIntersectingKey: key ? `is${capKey}ElementIntersecting` : 'isElementIntersecting',
+ }
- return visibilityStates
+ return {
+ [propertyNames.setRefKey]: setElementRef.current,
+ [propertyNames.isIntersectingKey]: isIntersecting,
+ [propertyNames.elementKey]: element,
+ } as IntersectionObserverResult
}
diff --git a/src/lib/use-multi-intersection-observer/index.test.tsx b/src/lib/use-multi-intersection-observer/index.test.tsx
new file mode 100644
index 0000000..1f4fca7
--- /dev/null
+++ b/src/lib/use-multi-intersection-observer/index.test.tsx
@@ -0,0 +1,133 @@
+import { act, renderHook } from '@testing-library/react'
+import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
+
+import useMultipleIntersectionObserver from '.'
+
+// Mock IntersectionObserver
+const mockIntersectionObserver = vi.fn()
+const mockObserve = vi.fn()
+const mockUnobserve = vi.fn()
+const mockDisconnect = vi.fn()
+
+beforeEach(() => {
+ mockIntersectionObserver.mockImplementation((callback) => ({
+ observe: mockObserve,
+ unobserve: mockUnobserve,
+ disconnect: mockDisconnect,
+ // Store callback for manual triggering
+ _callback: callback,
+ }))
+
+ // Mock window.IntersectionObserver
+ Object.defineProperty(window, 'IntersectionObserver', {
+ writable: true,
+ configurable: true,
+ value: mockIntersectionObserver,
+ })
+})
+
+afterEach(() => {
+ vi.clearAllMocks()
+})
+
+// Helper function to trigger intersection
+const forcefullyTriggerIntersection = (entry?: Partial, mockIndex = 0) => {
+ const mockInstance = mockIntersectionObserver.mock.results[mockIndex]?.value
+ mockInstance._callback([
+ {
+ isIntersecting: !!entry?.isIntersecting,
+ target: entry?.target || document.createElement('div'),
+ },
+ ])
+}
+
+describe('useMultipleIntersectionObserver', () => {
+ it('creates observers for multiple keys', () => {
+ const { result } = renderHook(() => useMultipleIntersectionObserver(['header', 'footer', 'sidebar']))
+
+ expect(result.current.header).toHaveProperty('setHeaderElementRef')
+ expect(result.current.header).toHaveProperty('isHeaderElementIntersecting')
+ expect(result.current.header).toHaveProperty('headerElement')
+
+ expect(result.current.footer).toHaveProperty('setFooterElementRef')
+ expect(result.current.sidebar).toHaveProperty('setSidebarElementRef')
+ })
+
+ it('passes options to all observers', () => {
+ const onIntersection = vi.fn()
+ const { result } = renderHook(() =>
+ useMultipleIntersectionObserver(['a', 'b'], {
+ threshold: 0.8,
+ onIntersection,
+ onlyTriggerOnce: true,
+ })
+ )
+
+ const elA = document.createElement('div')
+ const elB = document.createElement('div')
+
+ act(() => {
+ result.current.a.setAElementRef(elA)
+ result.current.b.setBElementRef(elB)
+ })
+
+ expect(mockIntersectionObserver).toHaveBeenCalledWith(
+ expect.any(Function),
+ expect.objectContaining({ threshold: 0.8 })
+ )
+
+ act(() => forcefullyTriggerIntersection({ isIntersecting: true, target: elA }))
+ expect(onIntersection).toHaveBeenCalledWith({ target: elA, isIntersecting: true })
+ expect(mockUnobserve).toHaveBeenCalledWith(elA) // onlyTriggerOnce
+ })
+
+ it('handles independent intersection states', () => {
+ const { result } = renderHook(() => useMultipleIntersectionObserver(['left', 'right']))
+
+ const leftEl = document.createElement('div')
+ const rightEl = document.createElement('div')
+
+ act(() => {
+ result.current.left.setLeftElementRef(leftEl)
+ result.current.right.setRightElementRef(rightEl)
+ })
+
+ // Trigger only left intersection
+ act(() => forcefullyTriggerIntersection({ isIntersecting: true, target: leftEl }))
+
+ expect(result.current.left.isLeftElementIntersecting).toBe(true)
+ expect(result.current.right.isRightElementIntersecting).toBe(false)
+
+ // Trigger right intersection
+ act(() => forcefullyTriggerIntersection({ isIntersecting: true, target: rightEl }, 1))
+ expect(result.current.left.isLeftElementIntersecting).toBe(true)
+ expect(result.current.right.isRightElementIntersecting).toBe(true)
+
+ // Again update the status
+ act(() => forcefullyTriggerIntersection({ isIntersecting: false, target: leftEl }))
+ expect(result.current.left.isLeftElementIntersecting).toBe(false)
+ expect(result.current.right.isRightElementIntersecting).toBe(true)
+
+ act(() => forcefullyTriggerIntersection({ isIntersecting: false, target: rightEl }, 1))
+ expect(result.current.left.isLeftElementIntersecting).toBe(false)
+ expect(result.current.right.isRightElementIntersecting).toBe(false)
+ })
+
+ it('works with empty keys array', () => {
+ const { result } = renderHook(() => useMultipleIntersectionObserver([]))
+ expect(result.current).toEqual({})
+ })
+
+ it('maintains type safety for returned observers', () => {
+ const { result } = renderHook(() => useMultipleIntersectionObserver(['nav', 'main'] as const))
+
+ // These should be accessible without TypeScript errors
+ result.current.nav.navElement
+ result.current.nav.setNavElementRef
+ result.current.nav.isNavElementIntersecting
+
+ result.current.main.mainElement
+ result.current.main.setMainElementRef
+ result.current.main.isMainElementIntersecting
+ })
+})
diff --git a/src/lib/use-multi-intersection-observer/index.tsx b/src/lib/use-multi-intersection-observer/index.tsx
new file mode 100644
index 0000000..0e9a2cc
--- /dev/null
+++ b/src/lib/use-multi-intersection-observer/index.tsx
@@ -0,0 +1,16 @@
+import type { IntersectionObserverOptions } from '../use-intersection-observer'
+
+import useIntersectionObserver from '../use-intersection-observer'
+
+// Additional utility hook for multiple elements
+export default function useMultipleIntersectionObserver(
+ keys: readonly Key[],
+ options?: Omit
+) {
+ const observers = keys.reduce((acc, key) => {
+ acc[key] = useIntersectionObserver({ ...options, key })
+ return acc
+ }, {} as Record>>)
+
+ return observers
+}
diff --git a/src/types/index.ts b/src/types/index.ts
index 6a5c19e..6963922 100644
--- a/src/types/index.ts
+++ b/src/types/index.ts
@@ -7,10 +7,3 @@ export interface EvOptions extends AddEventListenerOptions {
shouldInjectEvent?: boolean | any
}
export type EvHandler = (event: Event) => void
-
-// use-intersection type
-export interface IntersectionOptions extends IntersectionObserverInit {
- only_trigger_once?: boolean | Array
-}
-export type IntersectionObserverTarget = () => HTMLElement | null
-export type IsTargetIntersecting = boolean
From 29977ff5ed131da95397af85a29eeb1ae530cd38 Mon Sep 17 00:00:00 2001
From: "github-actions[bot]"
<41898282+github-actions[bot]@users.noreply.github.com>
Date: Sat, 26 Jul 2025 08:08:47 +0000
Subject: [PATCH 39/81] Version Packages (canary)
---
.changeset/pre.json | 1 +
CHANGELOG.md | 7 +++++++
package.json | 2 +-
3 files changed, 9 insertions(+), 1 deletion(-)
diff --git a/.changeset/pre.json b/.changeset/pre.json
index 283f7e9..3460f09 100644
--- a/.changeset/pre.json
+++ b/.changeset/pre.json
@@ -12,6 +12,7 @@
"good-vans-impress",
"hungry-moons-serve",
"icy-boats-make",
+ "modern-ants-add",
"pink-pugs-eat",
"popular-students-happen",
"tender-sites-occur",
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 640c4d2..37dabcf 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,12 @@
# classic-react-hooks
+## 2.0.0-canary.11
+
+### Minor Changes
+
+- 4f5bb6c: - breaking: use the implementation and working for `useIntersection` observer hook.
+ - feat: introduce new hook `useMultiIntersectionObserver` hook.
+
## 2.0.0-canary.10
### Patch Changes
diff --git a/package.json b/package.json
index 04da100..252028d 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "classic-react-hooks",
- "version": "2.0.0-canary.10",
+ "version": "2.0.0-canary.11",
"description": "A great collection of React utility hooks",
"keywords": [
"classic-react-hooks",
From 5877980f0d0e5f0441a8d84b9b81ce6987b9ea1b Mon Sep 17 00:00:00 2001
From: Ashish-simpleCoder
Date: Tue, 29 Jul 2025 23:14:36 +0530
Subject: [PATCH 40/81] docs: [wip] new docs
---
.changeset/stale-radios-rule.md | 6 ++
apps/doc/hooks/use-event-listener.md | 85 ++++++++++++++--
apps/doc/hooks/use-intersection-observer.md | 99 +++++++++++--------
.../use-intersection-observer/index.test.tsx | 4 +-
.../index.test.tsx | 2 +-
.../use-multi-intersection-observer/index.tsx | 1 -
6 files changed, 146 insertions(+), 51 deletions(-)
create mode 100644 .changeset/stale-radios-rule.md
diff --git a/.changeset/stale-radios-rule.md b/.changeset/stale-radios-rule.md
new file mode 100644
index 0000000..a34b42d
--- /dev/null
+++ b/.changeset/stale-radios-rule.md
@@ -0,0 +1,6 @@
+---
+'classic-react-hooks': patch
+---
+
+Docs: Start overhauling to new documentation
+- Create new docs for `use-event-listener` and `use-intersection-observer` hooks
diff --git a/apps/doc/hooks/use-event-listener.md b/apps/doc/hooks/use-event-listener.md
index f225102..a7cbef1 100644
--- a/apps/doc/hooks/use-event-listener.md
+++ b/apps/doc/hooks/use-event-listener.md
@@ -14,6 +14,39 @@ A React hook that provides a declarative way to add DOM event listeners with aut
- **Performance:** Event listeners are only attached when all conditions are met: target exists, handler is provided, and `shouldInjectEvent` is true
- **Standard options:** Full support for all `AddEventListenerOptions` (capture, once, passive, signal)
+## Problem It Solves
+
+::: info **Declarative API**
+
+- Event handling becomes part of component's declarative structure
+- Better integration with React's mental model
+ :::
+
+::: info **Boilerplate Reduction**
+
+- Eliminates repetitive `addEventListener/removeEventListener` code
+- Reduces component complexity by abstracting event handling logic
+- No need to manually manage cleanup in useEffect return functions.
+ Automatic cleanup ensures event listeners are removed when
+
+ -> Component unmounts
+
+ -> Target element changes
+
+ -> Event type changes
+
+ -> Options params `shouldInjectEvent`, `capture`, `once`, `passive`, `signal` gets changed
+
+:::
+
+::: info **Performance Benefits**
+
+- Stable references which prevent event listeners from being repeatedly added/removed
+
+- Efficient dependency tracking in the effect hooks
+
+:::
+
## Parameters
| Parameter | Type | Required | Default Value | Description |
@@ -24,7 +57,28 @@ A React hook that provides a declarative way to add DOM event listeners with aut
| options | [EvOptions](#types) | ❌ | undefined | Event listener options and feature flags |
| |
-### Types
+### Options Parameter
+
+The `options` parameter accepts an object that extends the standard `AddEventListenerOptions` with an additional custom property for conditional event handling.
+
+#### Standard AddEventListenerOptions
+
+| Property | Type | Default | Description |
+| --------- | ------------- | ----------- | -------------------------------------------------------------------------------- |
+| `capture` | `boolean` | `false` | If `true`, the listener will be triggered during the capture phase |
+| `once` | `boolean` | `false` | If `true`, the listener will be automatically removed after being triggered once |
+| `passive` | `boolean` | `false` | If `true`, indicates that the function will never call `preventDefault()` |
+| `signal` | `AbortSignal` | `undefined` | An AbortSignal that can be used to remove the event listener |
+
+#### Custom Options
+
+| Property | Type | Default | Description |
+| ------------------- | ---------------- | ------- | --------------------------------------------------------------------------------------------------- |
+| `shouldInjectEvent` | `boolean \| any` | `true` | Controls whether the event listener should be attached. When falsy, the event listener is not added |
+
+### Type Definitions
+
+::: details
```ts
export type EvTarget = () => EventTarget | null
@@ -42,11 +96,21 @@ export interface EvOptions extends AddEventListenerOptions {
}
```
+:::
+
+## Return Value(s)
+
+This hook does not return anything.
+
+| Return Value | Type | Description |
+| ------------ | ------ | ----------------------------------------------------------------------------------------------------- |
+| `void` | `void` | This hook does not return any value. It performs side effects only (adding/removing event listeners). |
+
## Usage Examples
### Basic Click Handler
-```ts
+```ts {5,7-13,15}
import { useRef } from 'react'
import { useEventListener } from 'classic-react-hooks'
@@ -65,9 +129,11 @@ export default function ClickExample() {
}
```
+::: details
+
### Window Events
-```ts
+```ts {6-9}
import { useEventListener } from 'classic-react-hooks'
export default function WindowExample() {
@@ -83,14 +149,18 @@ export default function WindowExample() {
}
```
+:::
+
### Conditional Event Listening
+::: details
+
```ts
import { useState } from 'react'
import { useEventListener } from 'classic-react-hooks'
export default function ConditionalExample() {
- const [isListening, setIsListening] = useState(true)
+ const [isListening, setIsListening] = useState(true) // [!code ++]
useEventListener({
target: () => document,
@@ -99,19 +169,22 @@ export default function ConditionalExample() {
console.log('Key pressed:', e.key)
},
options: {
- shouldInjectEvent: isListening, // Only listen when enabled
+ shouldInjectEvent: isListening, // Only listen when enabled // [!code ++]
},
})
return (
-
+ //
+ [!code ++]
Press any key (when listening is enabled)
)
}
```
+:::
+
## Common Use Cases
- Adding dom events (e.g 'click', 'keydown', 'resize')
diff --git a/apps/doc/hooks/use-intersection-observer.md b/apps/doc/hooks/use-intersection-observer.md
index 865f9b5..850114e 100644
--- a/apps/doc/hooks/use-intersection-observer.md
+++ b/apps/doc/hooks/use-intersection-observer.md
@@ -6,8 +6,6 @@ outline: deep
A React hook that provides a declarative way to observe element visibility using the Intersection Observer API with automatic cleanup and type-safe manner.
-## Browser Support
-
::: danger Important
This hook automatically checks for `IntersectionObserver` support and logs a warning in development if it's not available. The hook will gracefully handle unsupported browsers by not creating observers.
@@ -27,13 +25,68 @@ IntersectionObserver is supported in all modern browsers. For older browsers, yo
- **Performance:** Observer is only created when element exists and IntersectionObserver is supported
- **One-time observation:** Built-in support for observing elements only once
+## Problem It Solves
+
+::: info **Multiple Instance Management**
+
+- **Dynamic Key System:** Allows multiple intersection observers in the same component without naming conflicts
+- **Type-Safe Property Generation:** Each instance gets uniquely named properties with full TypeScript support
+- **Scalable Architecture:** Can observe unlimited elements without property collisions
+ :::
+
+::: info **Complex Intersection Observer Boilerplate**
+
+- **Eliminates** repetitive `IntersectionObserver` setup and teardown code
+- **Abstracts** away the complexity of observer lifecycle management
+- **Reduces** component code by `15-20 lines` per intersection observation
+ :::
+
+::: info **One-Time Observation Complexity**
+
+- `onlyTriggerOnce` option for automatically cleaning up observer after first intersection
+ :::
+
+::: info **Type Safety in Dynamic Scenarios**
+
+- Full TypeScript support with template literal types
+- IntelliSense support for dynamically generated property names
+
+```tsx
+// Without key
+const { element, setElementRef, isElementIntersecting } = useIntersectionObserver()
+
+// With key 'sidebar'
+const { sidebarElement, setSidebarElementRef, isSidebarElementIntersecting } = useIntersectionObserver({
+ key: 'sidebar',
+})
+```
+
+:::
+
+::: tip
+The hook uses `useSyncedRef` to avoid unnecessary re-renders when callback functions change
+:::
+
## Parameters
| Parameter | Type | Required | Default Value | Description |
| --------- | :-----------------------------------: | :------: | :-----------: | -------------------------------------------------- |
| options | [IntersectionObserverOptions](#types) | ❌ | undefined | Configuration object for the intersection observer |
-### Types
+### Options Parameter
+
+| Property | Type | Default | Description |
+| ----------------- | :------------------------------------------: | :---------: | ---------------------------------------------- |
+| `key` | `string` | `''` | Custom key for property naming |
+| `onIntersection` | `(entry: IntersectionObserverEntry) => void` | `undefined` | Callback fired on intersection changes |
+| `onlyTriggerOnce` | `boolean` | `false` | Whether to observe only the first intersection |
+| `root` | `Element \| Document \| null` | `null` | Root element for intersection |
+| `rootMargin` | `string` | `'0px'` | Margin around root element |
+| `threshold` | `number \| number[]` | `0` | Intersection ratio threshold(s) |
+
+### Type Definitions
+
+::: details
```ts
export interface BaseIntersectionObserverOptions {
@@ -59,18 +112,9 @@ export type IntersectionObserverResult = {
}
```
-### Options Properties
-
-| Property | Type | Default | Description |
-| ----------------- | :------------------------------------------: | :---------: | ---------------------------------------------- |
-| `key` | `string` | `''` | Custom key for property naming |
-| `onIntersection` | `(entry: IntersectionObserverEntry) => void` | `undefined` | Callback fired on intersection changes |
-| `onlyTriggerOnce` | `boolean` | `false` | Whether to observe only the first intersection |
-| `root` | `Element \| Document \| null` | `null` | Root element for intersection |
-| `rootMargin` | `string` | `'0px'` | Margin around root element |
-| `threshold` | `number \| number[]` | `0` | Intersection ratio threshold(s) |
+:::
-## Return Value
+## Return Value(s)
The hook returns an object with dynamically named properties based on the `key` parameter:
@@ -246,30 +290,3 @@ export default function MultipleThresholdsExample() {
- **Infinite scrolling:** Load more content when reaching the end
- **Sticky navigation:** Show/hide navigation based on hero section visibility
- **Performance optimization:** Pause expensive operations when elements are not visible
-
-## Performance Notes
-
-::: info
-
-- The hook uses `useSyncedRef` to avoid unnecessary re-renders when callback functions change
-- Observer instances are automatically cleaned up and recreated only when necessary
-- The `onlyTriggerOnce` option helps optimize performance by automatically disconnecting after first intersection
- :::
-
-## TypeScript Benefits
-
-The hook provides excellent TypeScript support:
-
-- **Dynamic property names:** Property names change based on the `key` parameter
-- **Type inference:** Return types are automatically inferred from the key
-- **Full IntersectionObserver API support:** All standard options are typed correctly
-
-```tsx
-// Without key
-const { element, setElementRef, isElementIntersecting } = useIntersectionObserver()
-
-// With key 'sidebar'
-const { sidebarElement, setSidebarElementRef, isSidebarElementIntersecting } = useIntersectionObserver({
- key: 'sidebar',
-})
-```
diff --git a/src/lib/use-intersection-observer/index.test.tsx b/src/lib/use-intersection-observer/index.test.tsx
index a5eea45..789e18e 100644
--- a/src/lib/use-intersection-observer/index.test.tsx
+++ b/src/lib/use-intersection-observer/index.test.tsx
@@ -41,7 +41,7 @@ const forcefullyTriggerIntersection = (entry?: Partial {
+describe('use-intersection-observer', () => {
describe('basic functionality', () => {
it('should return correct default property names when no key is provided', () => {
const { result } = renderHook(() => useIntersectionObserver())
@@ -380,7 +380,7 @@ function TestComponent({ hookKey }: { hookKey?: string }) {
)
}
-describe('useIntersectionObserver integration tests', () => {
+describe('use-intersection-observer integration tests', () => {
it('should work in a real component without key', () => {
render()
diff --git a/src/lib/use-multi-intersection-observer/index.test.tsx b/src/lib/use-multi-intersection-observer/index.test.tsx
index 1f4fca7..8ccfb99 100644
--- a/src/lib/use-multi-intersection-observer/index.test.tsx
+++ b/src/lib/use-multi-intersection-observer/index.test.tsx
@@ -41,7 +41,7 @@ const forcefullyTriggerIntersection = (entry?: Partial {
+describe('use-multi-intersection-observer', () => {
it('creates observers for multiple keys', () => {
const { result } = renderHook(() => useMultipleIntersectionObserver(['header', 'footer', 'sidebar']))
diff --git a/src/lib/use-multi-intersection-observer/index.tsx b/src/lib/use-multi-intersection-observer/index.tsx
index 0e9a2cc..9959edd 100644
--- a/src/lib/use-multi-intersection-observer/index.tsx
+++ b/src/lib/use-multi-intersection-observer/index.tsx
@@ -2,7 +2,6 @@ import type { IntersectionObserverOptions } from '../use-intersection-observer'
import useIntersectionObserver from '../use-intersection-observer'
-// Additional utility hook for multiple elements
export default function useMultipleIntersectionObserver(
keys: readonly Key[],
options?: Omit
From 214c673dbf986592b6c697ce33a3c277d303f4a4 Mon Sep 17 00:00:00 2001
From: "github-actions[bot]"
<41898282+github-actions[bot]@users.noreply.github.com>
Date: Tue, 29 Jul 2025 17:51:45 +0000
Subject: [PATCH 41/81] Version Packages (canary)
---
.changeset/pre.json | 1 +
CHANGELOG.md | 7 +++++++
package.json | 2 +-
3 files changed, 9 insertions(+), 1 deletion(-)
diff --git a/.changeset/pre.json b/.changeset/pre.json
index 3460f09..6810b58 100644
--- a/.changeset/pre.json
+++ b/.changeset/pre.json
@@ -15,6 +15,7 @@
"modern-ants-add",
"pink-pugs-eat",
"popular-students-happen",
+ "stale-radios-rule",
"tender-sites-occur",
"violet-cherries-applaud"
]
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 37dabcf..31f0f72 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,12 @@
# classic-react-hooks
+## 2.0.0-canary.12
+
+### Patch Changes
+
+- 5877980: Docs: Start overhauling to new documentation
+ - Create new docs for `use-event-listener` and `use-intersection-observer` hooks
+
## 2.0.0-canary.11
### Minor Changes
diff --git a/package.json b/package.json
index 252028d..e27d05c 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "classic-react-hooks",
- "version": "2.0.0-canary.11",
+ "version": "2.0.0-canary.12",
"description": "A great collection of React utility hooks",
"keywords": [
"classic-react-hooks",
From ff4615c33285ab7bb728c9eabc6695928d357922 Mon Sep 17 00:00:00 2001
From: Ashish-simpleCoder
Date: Thu, 31 Jul 2025 22:56:13 +0530
Subject: [PATCH 42/81] docs: revamp to new doc version for
use-multi-intersection-observer
---
.changeset/warm-olives-punch.md | 5 +
.../hooks/use-multi-intersection-observer.md | 155 ++++++++++--------
2 files changed, 94 insertions(+), 66 deletions(-)
create mode 100644 .changeset/warm-olives-punch.md
diff --git a/.changeset/warm-olives-punch.md b/.changeset/warm-olives-punch.md
new file mode 100644
index 0000000..4cd50cb
--- /dev/null
+++ b/.changeset/warm-olives-punch.md
@@ -0,0 +1,5 @@
+---
+'classic-react-hooks': minor
+---
+
+docs: revamp to new doc version for `use-multi-intersection-observer`
diff --git a/apps/doc/hooks/use-multi-intersection-observer.md b/apps/doc/hooks/use-multi-intersection-observer.md
index 19abfd8..89bb158 100644
--- a/apps/doc/hooks/use-multi-intersection-observer.md
+++ b/apps/doc/hooks/use-multi-intersection-observer.md
@@ -7,7 +7,7 @@ outline: deep
A React hook that provides a convenient way to observe multiple elements simultaneously using the Intersection Observer API. Built on top of `useIntersectionObserver` for consistent behavior and TypeScript support.
::: warning
-This hook requires you to understand about `useIntersectionHook`. As it is built on top it, so it will help you to better understand about features of this hook. Read here [useIntersectionHook](use-intersection-observer.html)
+This hook requires you to understand the working of `useIntersectionObserver` hook. As it is built on top it, so it will help you to better understand about features of this hook. Read here [useIntersectionObserver](use-intersection-observer.html)
:::
## Features
@@ -19,26 +19,84 @@ This hook requires you to understand about `useIntersectionHook`. As it is built
- **Auto cleanup:** All observers are automatically cleaned up on unmount
- **Performance optimized:** Each observer is independently managed for optimal performance
-## Parameters
+## Problem It Solves
-| Parameter | Type | Required | Default Value | Description |
-| --------- | :-------------------------------: | :------: | :-----------: | ------------------------------------------------- |
-| keys | `readonly Key[]` | ✅ | - | Array of unique keys for creating named observers |
-| options | [MultipleObserverOptions](#types) | ❌ | undefined | Shared configuration for all observers |
+::: info Multiple Hook Instance Boilerplate
+**_Problem:_** Managing many intersection observers requires repetitive hook calls
-### Types
+```ts
+// ❌ Without your hook - repetitive and verbose
+const hero = useIntersectionObserver({ key: 'hero', threshold: 0.5 })
+const about = useIntersectionObserver({ key: 'about', threshold: 0.5 })
+const services = useIntersectionObserver({ key: 'services', threshold: 0.5 })
+const contact = useIntersectionObserver({ key: 'contact', threshold: 0.5 })
+```
+
+**_Solution:_** Single hook call for multiple observers
```ts
-export type MultipleObserverOptions = Omit
+// ✅ With your hook - clean and DRY
+const sections = useMultipleIntersectionObserver(['hero', 'about', 'services', 'contact'], { threshold: 0.5 })
+```
-// Return type is a record where each key maps to its observer result
-type MultipleIntersectionObserverResult = Record<
- Key,
- ReturnType>
->
+:::
+
+::: info Memory and Performance Optimization
+**_Problem:_** Managing lifecycle of multiple observers manually
+
+- Risk of memory leaks with multiple observer instances
+- Complex cleanup logic for dynamic observer sets
+
+**_Solution:_** Automated lifecycle management
+
+- Leverages the proven cleanup logic of the base hook
+- Efficient memory usage through shared configuration
+ :::
+
+::: info Component Organization and Maintainability
+**_Problem:_** Managing many observer hooks clutters component logic
+
+- Multiple hook calls at component top level
+- Scattered observer logic throughout component
+- Difficult to understand observer relationships
+
+**_Solution:_** Clean, organized observer management
+
+- Single hook call consolidates all observer logic
+- Clear relationship between observed elements
+- Easier to reason about component behavior
+ :::
+
+::: info Type Safety at Scale
+**_Problem:_** Maintaining type safety with multiple dynamically named properties
+
+- Lost type inference when managing multiple observers manually
+- No IntelliSense for dynamically generated property names
+- Runtime errors from typos in property access
+
+**_Solution:_** Full type safety across all observers
+
+```ts
+// ✅ Full type safety and IntelliSense
+const observers = useMultipleIntersectionObserver(['hero', 'footer'] as const)
+// TypeScript knows: observers.hero.setHeroElementRef, observers.hero.isHeroElementIntersecting
+// TypeScript knows: observers.footer.setFooterElementRef, observers.footer.isFooterElementIntersecting
```
-### Options Properties
+:::
+
+::: tip
+For better performance with many elements, consider grouping related observations or using a single observer with multiple targets if the behavior is identical.
+:::
+
+## Parameters
+
+| Parameter | Type | Required | Default Value | Description |
+| --------- | :-------------------------------: | :------: | :-----------: | ------------------------------------------------- |
+| keys | `readonly Key[]` | ✅ | - | Array of unique keys for creating named observers |
+| options | [MultipleObserverOptions](#types) | ❌ | undefined | Shared configuration for all observers |
+
+### Options Parameter
All options from `useIntersectionObserver` except `key` (which is provided via the `keys` array):
@@ -50,21 +108,23 @@ All options from `useIntersectionObserver` except `key` (which is provided via t
| `rootMargin` | `string` | `'0px'` | Margin around root element |
| `threshold` | `number \| number[]` | `0` | Intersection ratio threshold(s) |
-## Return Value
+### Type Definitions
-The hook returns a record object where each key from the input array maps to its corresponding intersection observer result:
+::: details
```ts
-{
- [key]: {
- [`${key}Element`]: HTMLElement | null,
- [`set${Capitalize}ElementRef`]: (element: HTMLElement | null) => void,
- [`is${Capitalize}ElementIntersecting`]: boolean
- }
-}
+export type MultipleObserverOptions = Omit
+
+// Return type is a record where each key maps to its observer result
+type MultipleIntersectionObserverResult = Record<
+ Key,
+ ReturnType>
+>
```
-## Return Value
+:::
+
+## Return Value(s)
The hook returns a record object where each key from the input array maps to its corresponding intersection observer result:
@@ -83,7 +143,7 @@ The hook returns a record object where each key from the input array maps to its
```
::: info
-**`{key}Element`:** Holds the element reference which is being observed, it's initial undefined.
+**`{key}Element`:** Holds the element reference which is being observed, it's initially undefined.
**`set{Capitalize}ElementRef`:** Setter function to store the element reference within `element`, which is going tobe observed.
@@ -151,6 +211,10 @@ export default function MultipleObserversExample() {
}
```
+::: danger Important
+Each key in the array creates a separate `useIntersectionObserver` instance. While this provides maximum flexibility, consider the performance impact when observing many elements simultaneously.
+:::
+
### Navigation Visibility Tracker
::: details
@@ -248,44 +312,3 @@ export default function NavigationTracker() {
- **Animation choreography:** Coordinate animations across multiple elements
- **Performance monitoring:** Track which sections users actually view
- **Infinite scroll sections:** Manage multiple loading zones in complex layouts
-
-## Performance Considerations
-
-::: danger Important
-Each key in the array creates a separate `useIntersectionObserver` instance. While this provides maximum flexibility, consider the performance impact when observing many elements simultaneously.
-:::
-
-::: tip
-For better performance with many elements, consider grouping related observations or using a single observer with multiple targets if the behavior is identical.
-:::
-
-## TypeScript Benefits
-
-The hook provides excellent TypeScript support with:
-
-- **Readonly keys array:** Ensures keys are treated as literal types for better inference
-- **Mapped return types:** Each key gets properly typed observer properties
-- **Consistent API:** All observers follow the same naming pattern as the base hook
-
-```tsx
-// Type-safe usage
-const observers = useMultipleIntersectionObserver(['hero', 'about'] as const)
-
-// TypeScript knows these properties exist:
-observers.hero.setHeroElementRef
-observers.hero.isHeroElementIntersecting
-observers.about.setAboutElementRef
-observers.about.isAboutElementIntersecting
-```
-
-## Relationship to useIntersectionObserver
-
-::: details
-
-This hook is a thin wrapper around `useIntersectionObserver` that:
-
-- Creates multiple observer instances with the same configuration
-- Provides a convenient API for managing related observations
-- Maintains all the features and behavior of the base hook
-- Uses the same TypeScript patterns for consistent developer experience
- :::
From d9055563ec6ea6063c882ea67dbbe2825b59d3c5 Mon Sep 17 00:00:00 2001
From: "github-actions[bot]"
<41898282+github-actions[bot]@users.noreply.github.com>
Date: Thu, 31 Jul 2025 17:33:12 +0000
Subject: [PATCH 43/81] Version Packages (canary)
---
.changeset/pre.json | 3 ++-
CHANGELOG.md | 6 ++++++
package.json | 2 +-
3 files changed, 9 insertions(+), 2 deletions(-)
diff --git a/.changeset/pre.json b/.changeset/pre.json
index 6810b58..3d706ce 100644
--- a/.changeset/pre.json
+++ b/.changeset/pre.json
@@ -17,6 +17,7 @@
"popular-students-happen",
"stale-radios-rule",
"tender-sites-occur",
- "violet-cherries-applaud"
+ "violet-cherries-applaud",
+ "warm-olives-punch"
]
}
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 31f0f72..248bcd6 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,11 @@
# classic-react-hooks
+## 2.0.0-canary.13
+
+### Minor Changes
+
+- ff4615c: docs: revamp to new doc version for `use-multi-intersection-observer`
+
## 2.0.0-canary.12
### Patch Changes
diff --git a/package.json b/package.json
index e27d05c..3e6ad16 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "classic-react-hooks",
- "version": "2.0.0-canary.12",
+ "version": "2.0.0-canary.13",
"description": "A great collection of React utility hooks",
"keywords": [
"classic-react-hooks",
From a2f97446a3cd776eb1658c775ac1bf0e93953d98 Mon Sep 17 00:00:00 2001
From: Ashish-simpleCoder
Date: Mon, 22 Sep 2025 20:58:23 +0530
Subject: [PATCH 44/81] docs: major rewrite of the documentation
---
.changeset/brown-wolves-play.md | 28 ++
apps/doc/.vitepress/config.mts | 4 +-
.../CanReachToInternetCtxProvider.md | 131 ++-------
apps/doc/getting-started/overview.md | 14 +-
apps/doc/hooks/use-can-reach-to-internet.md | 126 +++++----
apps/doc/hooks/use-copy-to-clipboard.md | 44 +--
apps/doc/hooks/use-counter.md | 2 +-
apps/doc/hooks/use-debounced-fn.md | 187 +++++++++----
apps/doc/hooks/use-event-listener.md | 125 +++++----
apps/doc/hooks/use-intersection-observer.md | 156 ++++-------
apps/doc/hooks/use-interval-effect.md | 33 +--
.../hooks/use-multi-intersection-observer.md | 229 +++++----------
apps/doc/hooks/use-on-mount-effect.md | 24 +-
apps/doc/hooks/use-outside-click.md | 76 +++--
apps/doc/hooks/use-synced-effect.md | 32 ++-
apps/doc/hooks/use-synced-ref.md | 170 ++++++++----
apps/doc/hooks/use-throttled-fn.md | 261 ++++++++++++++----
apps/doc/hooks/use-timeout-effect.md | 20 +-
apps/doc/hooks/use-window-resize.md | 89 +++---
apps/doc/index.md | 20 +-
20 files changed, 964 insertions(+), 807 deletions(-)
create mode 100644 .changeset/brown-wolves-play.md
diff --git a/.changeset/brown-wolves-play.md b/.changeset/brown-wolves-play.md
new file mode 100644
index 0000000..8c8dded
--- /dev/null
+++ b/.changeset/brown-wolves-play.md
@@ -0,0 +1,28 @@
+---
+'classic-react-hooks': minor
+---
+
+## Fixes following issues
+- Fix: use-can-reach-to-internet `subscribe` handler for `useSyncExternalStore`. It was adding events instead of removing.
+- Fix: Prevent from re-triggering the call of `checkIfCanReachToInternet` function in useEffect when `isNetworkPollingEnabled` is disabled.
+
+## Test cases
+- Wrote test cases for use-can-reach-to-internet and use-copy-to-clipboard hook. Previously not written.
+
+## Major Rewrite for the Documentation
+- home page and overview
+- use-can-reach-to-internet
+- use-copy-to-clipboard
+- use-counter
+- use-debounced-fn
+- use-event-listener
+- use-intersection-observer
+- use-multi-intersection-observer
+- use-interval-effect
+- use-on-mount-effect
+- use-outside-effect
+- use-synced-effect
+- use-synced-ref
+- use-throttled-fn
+- use-timeout-effect
+- use-window-resize
diff --git a/apps/doc/.vitepress/config.mts b/apps/doc/.vitepress/config.mts
index 37cb520..358ccc7 100644
--- a/apps/doc/.vitepress/config.mts
+++ b/apps/doc/.vitepress/config.mts
@@ -118,9 +118,7 @@ function sidebarGuide(): DefaultTheme.SidebarItem[] {
{ text: 'use-outside-click', link: 'use-outside-click' },
{ text: 'use-debounced-fn', link: 'use-debounced-fn' },
{ text: 'use-throttled-fn', link: 'use-throttled-fn' },
- { text: 'use-can-reach-to-internet', link: 'use-can-reach-to-internet' ,collapsed: true,items:[
- { text: 'CanReachToInternetCtxProvider', link: 'components/CanReachToInternetCtxProvider',base:'/' },
- ]},
+ { text: 'use-can-reach-to-internet', link: 'use-can-reach-to-internet' ,collapsed: true},
{ text: 'use-timeout-effect', link: 'use-timeout-effect' },
{ text: 'use-interval-effect', link: 'use-interval-effect' },
{ text: 'use-synced-ref', link: 'use-synced-ref' },
diff --git a/apps/doc/components/CanReachToInternetCtxProvider.md b/apps/doc/components/CanReachToInternetCtxProvider.md
index b911ece..3eade04 100644
--- a/apps/doc/components/CanReachToInternetCtxProvider.md
+++ b/apps/doc/components/CanReachToInternetCtxProvider.md
@@ -4,7 +4,9 @@ outline: deep
# Internet Connectivity Context
-A React Context provider and hook for sharing internet connectivity status across your component tree without prop drilling. Built on top of the `useCanReachToInternet` hook to provide centralized connectivity monitoring.
+A React Context provider and hook for sharing internet connectivity status across your component tree without prop drilling.
+
+Built on top of the [useCanReachToInternet](/hooks/use-can-reach-to-internet.html) hook to provide centralized connectivity monitoring.
## Features
@@ -18,32 +20,32 @@ A React Context provider and hook for sharing internet connectivity status acros
A context provider component that wraps your application to provide connectivity status to all child components.
-#### Props
+It takes following props
-| Parameter | Type | Required | Default Value | Description |
-| ---------------------- | :---------: | :------: | :--------------------: | --------------------------------------------------------------------------- |
-| children | `ReactNode` | ✅ | - | React children components that will have access to the connectivity context |
-| enableNetworkPolling | `boolean` | ❌ | `true` | Enable automatic network polling to continuously check connectivity |
-| networkPollingInterval | `number` | ❌ | `3000` | Interval in milliseconds between network polls |
-| testUrl | `string` | ❌ | `'https://dns.google'` | URL to test internet connectivity against |
+| Parameter | Type | Required | Default Value | Description |
+| ---------------------- | :-------: | :------: | :----------------: | --------------------------------------------------------------------------- |
+| children | ReactNode | ✅ | - | React children components that will have access to the connectivity context |
+| enableNetworkPolling | boolean | ❌ | true | Enable automatic network polling to continuously check connectivity |
+| networkPollingInterval | number | ❌ | 3000 | Interval in milliseconds between network polls |
+| testUrl | string | ❌ | https://dns.google | URL to test internet connectivity against |
### useCanReachToInternetCtx
A custom hook to consume the internet connectivity context values.
-#### Returns
+Return value(s):
-| Property | Type | Description |
-| ----------------------------- | --------------- | -------------------------------------------------------------------------------- |
-| `isOnline` | `boolean` | Browser's native online/offline status from `navigator.onLine` |
-| `canReachToInternet` | `boolean` | Whether the device can actually reach the internet (verified via HTTP request) |
-| `isFullyConnected` | `boolean` | Combined status: `true` when both `isOnline` and `canReachToInternet` are `true` |
-| `isNetworkPollingEnabled` | `boolean` | Current state of automatic network polling |
-| `isCheckingConnection` | `boolean` | Whether a connectivity check is currently in progress |
-| `startNetworkPolling` | `() => void` | Function to start automatic network polling |
-| `stopNetworkPolling` | `() => void` | Function to stop automatic network polling |
-| `forceCheckNetwork` | `() => void` | Function to manually trigger a connectivity check |
-| `getCanReachToInternetStatus` | `() => boolean` | Function to get current internet reachability status |
+| Property | Type | Description |
+| --------------------------- | ------------- | -------------------------------------------------------------------------------- |
+| isOnline | boolean | Browser's native online/offline status from `navigator.onLine` |
+| canReachToInternet | boolean | Whether the device can actually reach the internet (verified via HTTP request) |
+| isFullyConnected | boolean | Combined status: `true` when both `isOnline` and `canReachToInternet` are `true` |
+| isNetworkPollingEnabled | boolean | Current state of automatic network polling |
+| isCheckingConnection | boolean | Whether a connectivity check is currently in progress |
+| startNetworkPolling | () => void | Function to start automatic network polling |
+| stopNetworkPolling | () => void | Function to stop automatic network polling |
+| forceCheckNetwork | () => void | Function to manually trigger a connectivity check |
+| getCanReachToInternetStatus | () => boolean | Function to get current internet reachability status |
## Usage Examples
@@ -65,6 +67,8 @@ function App() {
### Custom Configuration
+::: details Example
+
```tsx
import { CanReachToInternetCtxProvider } from 'classic-react-hooks'
@@ -83,89 +87,4 @@ function App() {
}
```
-### Network Status Badge Component
-
-```tsx
-import { useCanReachToInternetCtx } from 'classic-react-hooks'
-
-function NetworkStatusBadge() {
- const { isFullyConnected, isCheckingConnection } = useCanReachToInternetCtx()
-
- if (isCheckingConnection) {
- return
Checking...
- }
-
- return (
-
- {isFullyConnected ? '🟢 Online' : '🔴 Offline'}
-
- )
-}
-```
-
-## Why Use Context Instead of Hook Directly?
-
-### Benefits of Context Approach
-
-- **Single source of truth:** One connectivity state shared across the entire app
-- **Performance:** Only one network polling instance running instead of multiple
-- **Consistency:** All components see the same connectivity status simultaneously
-- **Centralized configuration:** Set polling intervals and test URLs once at the app level
-- **Reduced complexity:** No need to pass connectivity props down through component trees
-
-### When to Use Each Approach
-
-**Use Context when:**
-
-- Multiple components need connectivity status
-- You want centralized connectivity configuration
-- Building a larger application with complex component hierarchy
-- Need consistent connectivity state across the app
-
-**Use Hook directly when:**
-
-- Only one or few components need connectivity status
-- Building simple components or libraries
-- Need different connectivity configurations for different parts of your app
-- Working with isolated features
-
-## Important Notes
-
-### Error Handling
-
-The `useCanReachToInternetCtx` hook will throw an error if used outside of `CanReachToInternetCtxProvider`:
-
-```tsx
-// ❌ This will throw an error
-function ComponentOutsideProvider() {
- const { isOnline } = useCanReachToInternetCtx() // Error!
- return
-}
-```
-
-### Performance Considerations
-
-- **Single polling instance:** Context ensures only one network polling operation runs, regardless of how many components consume the context
-- **Memory efficiency:** Automatic cleanup of network requests and timers when provider unmounts
-- **Battery optimization:** Configure appropriate polling intervals for mobile devices
-
-### Best Practices
-
-- Place the provider as high as possible in your component tree for maximum availability
-- Use appropriate polling intervals based on your app's needs (longer intervals for battery-sensitive apps)
-- Consider conditional rendering for offline scenarios to improve user experience
-- Implement proper loading states using `isCheckingConnection` for better UX
+:::
diff --git a/apps/doc/getting-started/overview.md b/apps/doc/getting-started/overview.md
index 46bcaf8..8afad26 100644
--- a/apps/doc/getting-started/overview.md
+++ b/apps/doc/getting-started/overview.md
@@ -1,10 +1,6 @@
# Overview
-**_`classic-react-hooks`_** is a robust library of custom React hooks and components, offering powerful and efficient APIs to help you build features in a more declarative, modular, and maintainable way.
-
-## What is classic-react-hooks?
-
-**_`classic-react-hooks`_** is a collection of feature-rich custom React hooks designed to simplify your daily development tasks.
+**`classic-react-hooks`** is a minimal library of feature-rich custom React hooks and components designed to simplify daily development, offering powerful and efficient APIs to help you build features in a more declarative, modular, and maintainable way.
It encourages a clean, declarative and modular coding style that's easy to write, maintain, and scale.
@@ -29,11 +25,3 @@ bun add classic-react-hooks
```
:::
-
-## Features
-
-- 📚 Comprehensive collection of custom hooks
-- 🛡️ Type-safe (built with TypeScript)
-- 🌲 Tree-shakable for optimized bundle size
-- 📦 Zero external dependencies
-- ⚡ Lightweight, minimal, and easy to integrate
diff --git a/apps/doc/hooks/use-can-reach-to-internet.md b/apps/doc/hooks/use-can-reach-to-internet.md
index 2d477da..0107ee5 100644
--- a/apps/doc/hooks/use-can-reach-to-internet.md
+++ b/apps/doc/hooks/use-can-reach-to-internet.md
@@ -14,13 +14,58 @@ A comprehensive React hook for monitoring internet connectivity status that goes
- **Manual control:** Start/stop polling and force connectivity checks on demand
- **Cleanup handling:** Proper cleanup of network requests and timers to prevent memory leaks
+## Problem It Solves
+
+::: details The Problem with `navigator.onLine`
+
+**Problem:** `navigator.onLine` only tells you if the browser thinks it's connected to a network, not if it can actually reach the internet.
+
+Common Scenarios Where `navigator.onLine` Fails
+
+- **Limited Connectivity:** Your device is connected to a router, but the router has no internet connection. The browser sees the local network connection and reports online status as true.
+- **Network Issues:** DNS problems or ISP outages where you have network connection but can't reach to external servers.
+- **Captive Portals:** You're connected to WiFi at a hotel, airport but haven't authenticated yet. `navigator.onLine` returns true, but you can't access any websites.
+
+---
+
+**Solution:** How `useCanReachToInternet` solve these problems
+
+It provides two layers of connectivity detection
+
+- **`isOnline`:** Browser's basic network status (via `navigator.onLine`)
+- **`canReachToInternet`:** Actual internet reachability (via real HTTP requests to a test server)
+- **`isFullyConnected`:** Both conditions must be true for genuine internet access
+ :::
+
+## Important Notes
+
+::: danger Important
+
+- Performance Considerations:
+ - Network polling makes regular HTTP requests - use appropriate intervals
+ - Consider battery usage on mobile devices with frequent polling
+ - The hook automatically cleans up requests to prevent memory leaks
+- CORS Limitations:
+ - Uses `mode: 'no-cors'` for broader compatibility
+ - Default test URL `(https://dns.google)` is chosen for reliability
+
+:::
+
## Parameters
-| Parameter | Type | Required | Default Value | Description |
-| --------- | :---------------------------------: | :------: | :-----------: | -------------------------------------------------- |
-| options | [CanReachToInternetOptions](#types) | ❌ | {} | Configuration object for customizing hook behavior |
+| Parameter | Type | Required | Default Value | Description |
+| --------- | :--------------------------------------------: | :------: | :-----------: | -------------------------------------------------- |
+| options | [CanReachToInternetOptions](#type-definitions) | ❌ | {} | Configuration object for customizing hook behavior |
-### Parameter Types
+### Options Parameter
+
+| Property | Type | Default | Description |
+| ---------------------- | ------- | ------------------- | ----------------------------------------------------------------------------------------------------------------- |
+| enableNetworkPolling | boolean | true | Controls whether the hook should automatically and continuously check internet connectivity at regular intervals. |
+| networkPollingInterval | number | 3000 | Specifies the interval in milliseconds for polling. |
+| testUrl | string | https://dns.google' | The URL endpoint used to test actual internet connectivity with HEAD method. |
+
+### Type Definitions
```ts
type CanReachToInternetOptions = {
@@ -35,19 +80,27 @@ type CanReachToInternetOptions = {
type CanReachToInternetBoolean = boolean
```
-## Returns
+## Return value(s)
-| Property | Type | Description |
-| ----------------------------- | --------------- | -------------------------------------------------------------------------------- |
-| `isOnline` | `boolean` | Browser's native online/offline status from `navigator.onLine` |
-| `canReachToInternet` | `boolean` | Whether the device can actually reach the internet (verified via HTTP request) |
-| `isFullyConnected` | `boolean` | Combined status: `true` when both `isOnline` and `canReachToInternet` are `true` |
-| `isNetworkPollingEnabled` | `boolean` | Current state of automatic network polling |
-| `isCheckingConnection` | `boolean` | Whether a connectivity check is currently in progress |
-| `startNetworkPolling` | `() => void` | Function to start automatic network polling |
-| `stopNetworkPolling` | `() => void` | Function to stop automatic network polling |
-| `forceCheckNetwork` | `() => void` | Function to manually trigger a connectivity check |
-| `getCanReachToInternetStatus` | `() => boolean` | Function to get current internet reachability status |
+This hook provides full list of status flags and callbacks for internet reachability tracking
+
+| Property | Type | Description |
+| --------------------------- | ------------- | -------------------------------------------------------------------------------- |
+| isOnline | boolean | Browser's native online/offline status from `navigator.onLine` |
+| canReachToInternet | boolean | Whether the device can actually reach the internet (verified via HTTP request) |
+| isFullyConnected | boolean | Combined status: `true` when both `isOnline` and `canReachToInternet` are `true` |
+| isNetworkPollingEnabled | boolean | Current state of automatic network polling |
+| isCheckingConnection | boolean | Whether a connectivity check is currently in progress |
+| startNetworkPolling | () => void | Function to start automatic network polling |
+| stopNetworkPolling | () => void | Function to stop automatic network polling |
+| forceCheckNetwork | () => void | Function to manually trigger a connectivity check |
+| getCanReachToInternetStatus | () => boolean | Function to get current internet reachability status |
+
+## Common Use Cases
+
+- **Real Internet stats:** Show connection status, disable features when offline
+- **Error handling:** Distinguish between network errors and server errors
+- **Auto-retry logic:** Retry failed requests when connectivity is restored
## Usage Examples
@@ -71,6 +124,8 @@ function NetworkStatus() {
### Conditional Rendering Based on Connectivity
+::: details Example
+
```ts
import { useCanReachToInternet } from 'classic-react-hooks'
@@ -94,41 +149,4 @@ function DataFetchingComponent() {
}
```
-## Problem It Solves
-
-### The Problem with `navigator.onLine`
-
-`navigator.onLine` only tells you if the browser thinks it's connected to a network, not if it can actually reach the internet.
-
-#### Common Scenarios Where `navigator.onLine` Fails
-
-- **Limited Connectivity:** Your device is connected to a router, but the router has no internet connection. The browser sees the local network connection and reports online status as true.
-- **Network Issues:** DNS problems or ISP outages where you have network connection but can't reach to external servers.
-- **Captive Portals:** You're connected to WiFi at a hotel, airport but haven't authenticated yet. `navigator.onLine` returns true, but you can't access any websites.
-
----
-
-### How `useCanReachToInternet` solve these problems
-
-It provides two layers of connectivity detection
-
-- **`isOnline`:** Browser's basic network status (via `navigator.onLine`)
-- **`canReachToInternet`:** Actual internet reachability (via real HTTP requests to a test server)
-- **`isFullyConnected`:** Both conditions must be true for genuine internet access
-
-## Common Use Cases
-
-- User experience: Show connection status, disable features when offline
-- Error handling: Distinguish between network errors and server errors
-- Auto-retry logic: Retry failed requests when connectivity is restored
-
-## Important Notes
-
-- Performance Considerations:
- - Network polling makes regular HTTP requests - use appropriate intervals
- - Consider battery usage on mobile devices with frequent polling
- - The hook automatically cleans up requests to prevent memory leaks
-- CORS Limitations:
- - Uses `mode: 'no-cors'` for broader compatibility
- - Some URLs might not work due to CORS policies
- - Default test URL `(https://dns.google)` is chosen for reliability
+:::
diff --git a/apps/doc/hooks/use-copy-to-clipboard.md b/apps/doc/hooks/use-copy-to-clipboard.md
index 0351f3f..da5a405 100644
--- a/apps/doc/hooks/use-copy-to-clipboard.md
+++ b/apps/doc/hooks/use-copy-to-clipboard.md
@@ -8,7 +8,7 @@ A React hook that provides a simple and reliable way to copy text to the clipboa
## Features
-- **Clipboard API Support:** Uses the modern `navigator.clipboard` API for secure clipboard access
+- **Clipboard API Support:** Uses the modern [navigator.clipboard](https://developer.mozilla.org/en-US/docs/Web/API/Clipboard_API) API for secure clipboard access
- **Fallback Handling:** Gracefully handles cases where clipboard API is not available
- **Success/Error Callbacks:** Built-in success and error handling with customizable callbacks
- **Flexible Configuration:** Configure global callbacks via props or override per-call
@@ -16,33 +16,45 @@ A React hook that provides a simple and reliable way to copy text to the clipboa
## Parameters
-| Parameter | Type | Required | Default Value | Description |
-| --------- | :---------------------------: | :------: | :-----------: | --------------------------------- |
-| onSuccess | [OnSuccess](#parameter-types) | ❌ | - | Default success callback function |
-| onError | [OnError](#parameter-types) | ❌ | - | Default error callback function |
+| Parameter | Type | Required | Default Value | Description |
+| --------- | :----------------------------: | :------: | :-----------: | --------------------------------- |
+| onSuccess | [OnSuccess](#type-definitions) | ❌ | - | Default success callback function |
+| onError | [OnError](#type-definitions) | ❌ | - | Default error callback function |
-### Parameter Types
+::: warning
+Any occured errors during operation are passed to the `onError` callback with descriptive error messages.
+:::
+
+### Type Definitions
```ts
type OnSuccess = () => void
type OnError = (err: Error) => void
```
-## Returns
+## Return Value(s)
+
+The hook returns a function which will copy the provided data into the clipboard
-- `copyToClipboard` - Handler function to copy text data
+| Return Value | Type | Description |
+| -------------------------- | ------------------- | ---------------------------------- |
+| `copyToClipboard` function | `CopyToClipboardFn` | Handler function to copy text data |
-### Return Types
+### Type Definitions
```ts
type CopyToClipboardFn = (data: string, onSuccess?: OnSuccess, onError?: OnError) => Promise
```
+## Common Use Cases
+
+- Copy text data programatically
+
## Usage Examples
### Basic usage
-```ts
+```ts {7-15}
import { useState } from 'react'
import { useCopyToClipboard } from 'classic-react-hooks'
@@ -67,10 +79,6 @@ export default function CopyButton() {
}
```
-## Common Use Cases
-
-- Copy text data
-
## Alternative: Non-React Usage
For use outside of React components, use the standalone function:
@@ -88,11 +96,3 @@ copyToClipboardFn(
(error) => console.error('Copy failed:', error)
)
```
-
-## Error Handling
-
-The hook handles various error scenarios:
-
-- **Clipboard API not available:** When `navigator.clipboard` is not supported
-
-All errors are passed to the `onError` callback with descriptive error messages.
diff --git a/apps/doc/hooks/use-counter.md b/apps/doc/hooks/use-counter.md
index 66332f0..9538581 100644
--- a/apps/doc/hooks/use-counter.md
+++ b/apps/doc/hooks/use-counter.md
@@ -21,7 +21,7 @@ A type-safe React hook for managing counter state with customizable step values
| initialValue | number | ❌ | 0 | Initial value for the counter. |
| stepper | number | ❌ | 1 | Amount to increment/decrement by on each operation. |
-## Returns
+## Return value(s)
Returns a type-safe object with dynamically named properties:
diff --git a/apps/doc/hooks/use-debounced-fn.md b/apps/doc/hooks/use-debounced-fn.md
index a86c1dc..03ac6f9 100644
--- a/apps/doc/hooks/use-debounced-fn.md
+++ b/apps/doc/hooks/use-debounced-fn.md
@@ -2,49 +2,164 @@
outline: deep
---
-# use-debouced-fn
+# use-debounced-fn
A React hook that returns a debounced version of any function, delaying its execution until after a specified delay has passed since the last time it was invoked.
-Perfect for optimizing performance in scenarios like search inputs, API calls, or resize handlers.
-
## Features
-- **Debouncing Functionality:** Delaying function execution until after a specified period of inactivity. Calling the function again before the delay expires, the previous call is cancelled and the timer resets.
-- **Configurable Delay:** You can specify a custom delay period, with a sensible default of 300ms.
-- **Dynamic Props Updates:** The hook properly handles updates to both the callback function and delay value during re-renders without losing the debouncing behavior.
-- **Performance optimized:** Prevents excessive function calls
-- **Auto cleanup:** Automatically clears timers on component unmount and on delay prop change
+- **Auto cleanup:** Timeouts are automatically cleared on unmount or dependency changes
+- **Flexible delay:** Configurable delay with sensible defaults
+- **Performance optimized:** Prevents excessive function calls during rapid user interactions
+- **Error handling:** Preserves original function's error behavior
+
+## Problem It Solves
+
+::: details **Boilerplate Reduction**
+
+**Problem:** Manually implementing debouncing in React components leads to lengthy, error-prone code with potential memory leaks and stale closures.
+
+```tsx
+// ❌ Problematic approach which is redundant and lengthy
+function SearchInput() {
+ const [query, setQuery] = useState('')
+ const [results, setResults] = useState([])
+ const timeoutRef = useRef()
+
+ const handleSearch = useCallback(async (searchTerm: string) => {
+ if (timeoutRef.current) {
+ clearTimeout(timeoutRef.current)
+ }
+
+ timeoutRef.current = setTimeout(async () => {
+ try {
+ if (searchTerm.trim()) {
+ const response = await fetch(`/api/search?q=${searchTerm}`)
+ const data = await response.json()
+ setResults(data.results)
+ }
+ } catch (error) {
+ console.error('Search failed:', error)
+ }
+ }, 500)
+ }, [])
+
+ useEffect(() => {
+ return () => {
+ if (timeoutRef.current) {
+ clearTimeout(timeoutRef.current) // Manual cleanup on unmount
+ }
+ }
+ }, [])
+
+ const handleInputChange = (e: React.ChangeEvent) => {
+ const value = e.target.value
+ setQuery(value)
+ handleSearch(value)
+ }
+
+ return
+}
+```
+
+**Solution:**
+
+- Eliminates repetitive debounce timing logic
+- Automatic cleanup ensures timeouts are cleared when:
+
+ → Component unmounts
+
+ → Delay value changes
+
+ → Function reference changes
+
+```tsx
+// ✅ Clean, declarative approach
+function SearchInput() {
+ const [query, setQuery] = useState('')
+ const [results, setResults] = useState([])
+
+ const debouncedSearch = useDebouncedFn({
+ callbackToBounce: async (searchTerm: string) => {
+ if (searchTerm.trim()) {
+ const response = await fetch(`/api/search?q=${searchTerm}`)
+ const data = await response.json()
+ setResults(data.results)
+ }
+ },
+ delay: 500,
+ })
+
+ const handleInputChange = (e: React.ChangeEvent) => {
+ const value = e.target.value
+ setQuery(value)
+ debouncedSearch(value)
+ }
+
+ return
+}
+```
+
+:::
+
+::: details **Performance Benefits**
+
+- **Reduces execution frequency:** Limits function calls during rapid user input
+- **Memory efficient:** Proper cleanup prevents memory leaks from pending timeouts
+- **Stable references:** Function reference remains stable across re-renders
+
+:::
## Parameters
-| Parameter | Type | Required | Default Value | Description |
-| ---------------- | :------: | :------: | :-----------: | ----------------------------------------------------- |
-| callbackToBounce | Function | ✅ | - | The function to be debounced |
-| delay | number | ❌ | 300 | Delay in milliseconds before the function is executed |
+| Parameter | Type | Required | Default Value | Description |
+| ---------------- | :------------------------------: | :------: | :-----------: | ----------------------------------------------- |
+| callbackToBounce | [DebouncedFn](#type-definitions) | ✅ | - | The function to debounce |
+| delay | number | ❌ | 300ms | Delay in milliseconds before function execution |
+
+### Type Definitions
+
+::: details
+
+```ts
+export type DebouncedFn any> = (...args: Parameters) => void
+```
+
+:::
-## Returns
+## Return Value(s)
-- Returns a debounced version of the provided function that will only execute after the specified delay has passed since the last invocation.
+The hook returns a debounced version of the provided callback.
+
+| Return Value | Type | Description |
+| ------------- | ---------------------------------- | --------------------------------------------------------------------------------------- |
+| `debouncedFn` | `(...args: Parameters) => void` | Debounced version of the original function that delays execution by the specified delay |
+
+## Common Use Cases
+
+- **Search functionality:** Debouncing search queries to reduce API calls
+- **API rate limiting:** Preventing excessive API requests
## Usage Examples
-### Basic debouncing
+### Basic Search Debouncing
-```ts
-import { useState, useEffect } from 'react'
+```tsx {10-17}
+import { useState } from 'react'
import { useDebouncedFn } from 'classic-react-hooks'
-export default function SearchInput() {
+export default function SearchExample() {
const [query, setQuery] = useState('')
const [results, setResults] = useState([])
const debouncedSearch = useDebouncedFn({
callbackToBounce: async (searchTerm: string) => {
if (searchTerm.trim()) {
- const response = await fetch(`https://dummyjson.com/users/search?q=${searchTerm}`)
+ const response = await fetch(`https://api.example.com/search?q=${searchTerm}`)
const data = await response.json()
setResults(data.results)
+ } else {
+ setResults([])
}
},
delay: 500,
@@ -56,14 +171,6 @@ export default function SearchInput() {
debouncedSearch(value)
}
- useEffect(() => {
- ;(async function () {
- const response = await fetch(`https://dummyjson.com/users`)
- const data = await response.json()
- setResults(data.results)
- })()
- }, [])
-
return (
@@ -76,29 +183,3 @@ export default function SearchInput() {
)
}
```
-
-## Common Use Cases
-
-- Delay API calls until user stops typing
-- Validate fields after user pauses input
-- Prevent excessive API calls
-
-## Alternative: Non-React Usage
-
-For use outside of React components, use the standalone wrapper:
-
-```ts
-import { debouncedFnWrapper } from 'classic-react-hooks'
-
-const { fn: debouncedLog, cleanup } = debouncedFnWrapper({
- callbackToBounce: (message: string) => console.log(message),
- delay: 1000,
-})
-
-// Use the debounced function
-debouncedLog('Hello')
-debouncedLog('World') // Only 'World' will be logged after 1 second
-
-// Clean up when done
-cleanup()
-```
diff --git a/apps/doc/hooks/use-event-listener.md b/apps/doc/hooks/use-event-listener.md
index a7cbef1..2c60b38 100644
--- a/apps/doc/hooks/use-event-listener.md
+++ b/apps/doc/hooks/use-event-listener.md
@@ -4,57 +4,87 @@ outline: deep
# use-event-listener
-A React hook that provides a declarative way to add DOM event listeners with automatic cleanup.
+A React hook which provides a simple and declarative way to add DOM event listeners with automatic cleanup.
## Features
-- **Auto cleanup:** Events are automatically removed on unmount or dependency changes
-- **Reactive:** The hook re-evaluates and potentially re-attaches listeners when any dependency changes (target, event, options)
-- **Conditional events:** Built-in support for conditionally enabling/disabling event
-- **Performance:** Event listeners are only attached when all conditions are met: target exists, handler is provided, and `shouldInjectEvent` is true
+- **Auto cleanup:** Automatic cleanup of events on unmount and dependency change
+- **Reactive:** Potentially re-attaches listeners on dependency change(target, event, options)
+- **Conditional event:** Conditional event support with feature flag. And listeners only get attached when:- target exists, handler is provided, and `shouldInjectEvent` is true
- **Standard options:** Full support for all `AddEventListenerOptions` (capture, once, passive, signal)
## Problem It Solves
-::: info **Declarative API**
+::: details **Boilerplate Reduction**
-- Event handling becomes part of component's declarative structure
-- Better integration with React's mental model
- :::
+- **Problem:** Manually managing event listeners in React components leads to verbose, repetitive and error-prone code with potential memory leaks.
-::: info **Boilerplate Reduction**
+```tsx
+// ❌ Problematic approach which is redundant and verbose
+function Component() {
+ const [scrollY, setScrollY] = useState(0)
+
+ useEffect(() => {
+ const handleScroll = () => {
+ setScrollY(window.scrollY)
+ }
+
+ window.addEventListener('scroll', handleScroll)
+ return () => window.removeEventListener('scroll', handleScroll) // Doing proper cleanup on unmount
+ }, [])
+
+ return
Current: {scrollY}
+}
+```
+
+**Solution:**
- Eliminates repetitive `addEventListener/removeEventListener` code
- Reduces component complexity by abstracting event handling logic
-- No need to manually manage cleanup in useEffect return functions.
- Automatic cleanup ensures event listeners are removed when
+- Automatic cleanup ensures listeners are removed when:-
-> Component unmounts
- -> Target element changes
+ -> `Target` element changes
+
+ -> `Event` type changes
+
+ -> Any of `Options` params:- `shouldInjectEvent`, `capture`, `once`, `passive`, `signal` gets changed
- -> Event type changes
+```tsx
+// ✅ Clean, declarative approach
+function Component() {
+ const [scrollY, setScrollY] = useState(0)
+ const breakpoint = useEventListener({
+ target: () => window,
+ event: 'scroll',
+ handler: () => {
+ setScrollY(window.scrollY)
+ },
+ })
- -> Options params `shouldInjectEvent`, `capture`, `once`, `passive`, `signal` gets changed
+ return
Current: {scrollY}
+}
+```
:::
-::: info **Performance Benefits**
+::: details **Performance Benefits**
-- Stable references which prevent event listeners from being repeatedly added/removed
+- Stable references accross re-renders which prevents listeners from being repeatedly added/removed
-- Efficient dependency tracking in the effect hooks
+- Efficient dependency tracking
:::
## Parameters
-| Parameter | Type | Required | Default Value | Description |
-| --------- | :-----------------: | :------: | :-----------: | ------------------------------------------------ |
-| target | [EvTarget](#types) | ✅ | - | Function that returns the target element or null |
-| event | string | ✅ | - | Event name (e.g., 'click', 'keydown', 'resize') |
-| handler | [EvHandler](#types) | ❌ | undefined | Event handler callback function |
-| options | [EvOptions](#types) | ❌ | undefined | Event listener options and feature flags |
+| Parameter | Type | Required | Default Value | Description |
+| --------- | :----------------------------: | :------: | :-----------: | ------------------------------------------------- |
+| target | [EvTarget](#type-definitions) | ✅ | - | Target element on which the event is listened to. |
+| event | string | ✅ | - | Event name (e.g. 'click', 'keydown') |
+| handler | [EvHandler](#type-definitions) | ❌ | undefined | Event listener callback function |
+| options | [EvOptions](#type-definitions) | ❌ | undefined | Event listener options and feature flags |
| |
### Options Parameter
@@ -63,18 +93,18 @@ The `options` parameter accepts an object that extends the standard `AddEventLis
#### Standard AddEventListenerOptions
-| Property | Type | Default | Description |
-| --------- | ------------- | ----------- | -------------------------------------------------------------------------------- |
-| `capture` | `boolean` | `false` | If `true`, the listener will be triggered during the capture phase |
-| `once` | `boolean` | `false` | If `true`, the listener will be automatically removed after being triggered once |
-| `passive` | `boolean` | `false` | If `true`, indicates that the function will never call `preventDefault()` |
-| `signal` | `AbortSignal` | `undefined` | An AbortSignal that can be used to remove the event listener |
+| Property | Type | Default | Description |
+| -------- | ----------- | --------- | -------------------------------------------------------------------------------- |
+| capture | boolean | false | If `true`, the listener will be triggered during the capture phase |
+| once | boolean | false | If `true`, the listener will be automatically removed after being triggered once |
+| passive | boolean | false | If `true`, indicates that the function will never call `preventDefault()` |
+| signal | AbortSignal | undefined | An AbortSignal that can be used to remove the event listener |
#### Custom Options
-| Property | Type | Default | Description |
-| ------------------- | ---------------- | ------- | --------------------------------------------------------------------------------------------------- |
-| `shouldInjectEvent` | `boolean \| any` | `true` | Controls whether the event listener should be attached. When falsy, the event listener is not added |
+| Property | Type | Default | Description |
+| ----------------- | -------------- | ------- | --------------------------------------------------------------------------------------------------- |
+| shouldInjectEvent | boolean \| any | true | Controls whether the event listener should be attached. When false, the event listener is not added |
### Type Definitions
@@ -86,10 +116,10 @@ export type EvHandler = (event: Event) => void
export interface EvOptions extends AddEventListenerOptions {
// Standard AddEventListenerOptions:
- // capture?: boolean
- // once?: boolean
- // passive?: boolean
- // signal?: AbortSignal
+ capture?: boolean
+ once?: boolean
+ passive?: boolean
+ signal?: AbortSignal
// Custom option:
shouldInjectEvent?: boolean | any // Controls whether the event should be attached
@@ -102,15 +132,15 @@ export interface EvOptions extends AddEventListenerOptions {
This hook does not return anything.
-| Return Value | Type | Description |
-| ------------ | ------ | ----------------------------------------------------------------------------------------------------- |
-| `void` | `void` | This hook does not return any value. It performs side effects only (adding/removing event listeners). |
+## Common Use Cases
+
+- Adding dom events (e.g 'click', 'keydown', 'resize', 'scroll')
## Usage Examples
### Basic Click Handler
-```ts {5,7-13,15}
+```ts {7-13}
import { useRef } from 'react'
import { useEventListener } from 'classic-react-hooks'
@@ -129,9 +159,9 @@ export default function ClickExample() {
}
```
-::: details
+### Listening Window Event
-### Window Events
+::: details Example
```ts {6-9}
import { useEventListener } from 'classic-react-hooks'
@@ -153,7 +183,7 @@ export default function WindowExample() {
### Conditional Event Listening
-::: details
+::: details Example
```ts
import { useState } from 'react'
@@ -175,8 +205,7 @@ export default function ConditionalExample() {
return (
- //
- [!code ++]
+
Press any key (when listening is enabled)
)
@@ -184,7 +213,3 @@ export default function ConditionalExample() {
```
:::
-
-## Common Use Cases
-
-- Adding dom events (e.g 'click', 'keydown', 'resize')
diff --git a/apps/doc/hooks/use-intersection-observer.md b/apps/doc/hooks/use-intersection-observer.md
index 850114e..90d1a9a 100644
--- a/apps/doc/hooks/use-intersection-observer.md
+++ b/apps/doc/hooks/use-intersection-observer.md
@@ -4,52 +4,31 @@ outline: deep
# use-intersection-observer
-A React hook that provides a declarative way to observe element visibility using the Intersection Observer API with automatic cleanup and type-safe manner.
+A React hook that provides a declarative way to observe element visibility using the [Intersection Observer](https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API) API.
::: danger Important
This hook automatically checks for `IntersectionObserver` support and logs a warning in development if it's not available. The hook will gracefully handle unsupported browsers by not creating observers.
:::
-::: info
-IntersectionObserver is supported in all modern browsers. For older browsers, you may need to include a polyfill.
-:::
-
## Features
-- **Auto cleanup:** Observer is automatically disconnected on unmount or element changes
-- **Reactive:** The hook re-evaluates and potentially re-creates observers when dependencies change
-- **Type-safe:** Full TypeScript support with dynamic property naming based on key
-- **Flexible keys:** Support for custom property naming through the `key` parameter
-- **Standard options:** Full support for all `IntersectionObserverInit` options (root, rootMargin, threshold)
-- **Performance:** Observer is only created when element exists and IntersectionObserver is supported
+- **Auto cleanup:** Automatic cleanup of observers on unmount and dependency change
+- **Reactive:** Potentially re-attaches observers on dependency change(element, onlyTriggerOnce)
+- **Flexible keys:** Support for custom property naming through the `key` parameter with full-type safety
- **One-time observation:** Built-in support for observing elements only once
+- **Standard options:** Full support for all `IntersectionObserverInit` options ([root](https://developer.mozilla.org/en-US/docs/Web/API/IntersectionObserver/root), [rootMargin](https://developer.mozilla.org/en-US/docs/Web/API/IntersectionObserver/rootMargin), [threshold](https://developer.mozilla.org/en-US/docs/Web/API/IntersectionObserver/thresholds))
+
+::: tip
+The hook uses `useSyncedRef` to avoid unnecessary re-renders when callback functions change
+:::
## Problem It Solves
-::: info **Multiple Instance Management**
+::: details **Multiple Instance Management with Type-Safety**
-- **Dynamic Key System:** Allows multiple intersection observers in the same component without naming conflicts
-- **Type-Safe Property Generation:** Each instance gets uniquely named properties with full TypeScript support
+- **Type-Safe & Dynamic Key Property Generation:** Each instance gets uniquely named properties with full TypeScript support and IntelliSense
- **Scalable Architecture:** Can observe unlimited elements without property collisions
- :::
-
-::: info **Complex Intersection Observer Boilerplate**
-
-- **Eliminates** repetitive `IntersectionObserver` setup and teardown code
-- **Abstracts** away the complexity of observer lifecycle management
-- **Reduces** component code by `15-20 lines` per intersection observation
- :::
-
-::: info **One-Time Observation Complexity**
-
-- `onlyTriggerOnce` option for automatically cleaning up observer after first intersection
- :::
-
-::: info **Type Safety in Dynamic Scenarios**
-
-- Full TypeScript support with template literal types
-- IntelliSense support for dynamically generated property names
```tsx
// Without key
@@ -63,26 +42,36 @@ const { sidebarElement, setSidebarElementRef, isSidebarElementIntersecting } = u
:::
-::: tip
-The hook uses `useSyncedRef` to avoid unnecessary re-renders when callback functions change
-:::
+::: details **One-Time Observation Complexity**
+
+- `onlyTriggerOnce` option for automatically cleaning up observer after first intersection
+ :::
## Parameters
-| Parameter | Type | Required | Default Value | Description |
-| --------- | :-----------------------------------: | :------: | :-----------: | -------------------------------------------------- |
-| options | [IntersectionObserverOptions](#types) | ❌ | undefined | Configuration object for the intersection observer |
+| Parameter | Type | Required | Default Value | Description |
+| --------- | :----------------------------------------------: | :------: | :-----------: | -------------------------------------------------- |
+| options | [IntersectionObserverOptions](#type-definitions) | ❌ | undefined | Configuration object for the intersection observer |
### Options Parameter
-| Property | Type | Default | Description |
-| ----------------- | :------------------------------------------: | :---------: | ---------------------------------------------- |
-| `key` | `string` | `''` | Custom key for property naming |
-| `onIntersection` | `(entry: IntersectionObserverEntry) => void` | `undefined` | Callback fired on intersection changes |
-| `onlyTriggerOnce` | `boolean` | `false` | Whether to observe only the first intersection |
-| `root` | `Element \| Document \| null` | `null` | Root element for intersection |
-| `rootMargin` | `string` | `'0px'` | Margin around root element |
-| `threshold` | `number \| number[]` | `0` | Intersection ratio threshold(s) |
+The `options` parameter accepts an object that extends the standard `IntersectionObserverInit` with an additional custom property for conditional event handling and post callback.
+
+#### Standard IntersectionObserverInit Options
+
+| Property | Type | Default | Description |
+| ---------- | :-------------------------: | :-----: | ------------------------------- |
+| key | string | '' | Custom key for property naming |
+| root | Element \| Document \| null | null | Root element for intersection |
+| rootMargin | string | 0px | Margin around root element |
+| threshold | number \| number[] | 0 | Intersection ratio threshold(s) |
+
+#### Custom Options
+
+| Property | Type | Default | Description |
+| --------------- | :----------------------------------------: | :-------: | --------------------------------------------------------- |
+| onlyTriggerOnce | boolean | false | Controls whether to observe only the initial intersection |
+| onIntersection | (entry: IntersectionObserverEntry) => void | undefined | Callback fired on every intersection of the element |
### Type Definitions
@@ -122,13 +111,19 @@ The hook returns an object with dynamically named properties based on the `key`
- **With key:** `{key}Element`, `set{Key}ElementRef`, `is{Key}ElementIntersecting`
::: info
-**`element`:** Holds the element reference which is being observed, it's initial undefined.
+**`element`:** Holds the reference of element which is being observed, initial value is undefined.
-**`setElementRef`:** Setter function to store the element reference within `element`, which is going tobe observed.
+**`setElementRef`:** A Setter function to store the element reference within `element`, which will be observed.
**`isElementIntersecting`:** Holds the boolean intersection status of the `element` weather it is intersecting the screen or not.
:::
+## Common Use Cases
+
+- **Lazy loading:** Load images or content when they come into view
+- **Infinite scrolling:** Load more content when reaching the end
+- **Performance optimization:** Pause expensive operations when elements are not visible
+
## Usage Examples
### Basic Intersection Observer
@@ -145,15 +140,9 @@ export default function BasicExample() {
})
return (
-
)
@@ -281,12 +252,3 @@ export default function MultipleThresholdsExample() {
```
:::
-
-## Common Use Cases
-
-- **Lazy loading:** Load images or content when they come into view
-- **Animation triggers:** Start animations when elements become visible
-- **Analytics:** Track when users view certain sections
-- **Infinite scrolling:** Load more content when reaching the end
-- **Sticky navigation:** Show/hide navigation based on hero section visibility
-- **Performance optimization:** Pause expensive operations when elements are not visible
diff --git a/apps/doc/hooks/use-interval-effect.md b/apps/doc/hooks/use-interval-effect.md
index aa81d47..20a0ce2 100644
--- a/apps/doc/hooks/use-interval-effect.md
+++ b/apps/doc/hooks/use-interval-effect.md
@@ -8,26 +8,27 @@ A React hook that executes a callback function at regular intervals, similar to
## Features
-- **Scheduled execution:** Executes a callback with a fixed time delay between each call
-- **Flexible:** Provides methods to clear or restart the timer
-- **Automatic Cleanup:** Automatically cleans up timer on component unmount
-- **Syncronization:** Syncs with the latest callback and timeout values
+- **Recurring execution:** Executes a callback at regular intervals
+- **Flexible control:** Provides methods to clear or restart the timer with different intervals
+- **Auto cleanup:** Automatically clears up interval on component unmount
## Parameters
-| Parameter | Type | Required | Default Value | Description |
-| --------- | :------: | :------: | :-----------: | ------------------------------------------------ |
-| handler | Function | ✅ | - | Callback function executed at each interval |
-| interval | number | ❌ | 100 | Time in milliseconds between callback executions |
+| Parameter | Type | Required | Default Value | Description |
+| --------- | :------: | :------: | :-----------: | --------------------------------------------------------------- |
+| handler | Function | ✅ | - | The callback function to execute at each interval |
+| interval | number | ❌ | 100 | The delay in milliseconds between each execution of the handler |
-## Returns
+## Return value(s)
-- Returns an object with control methods:
+This hooks returns an object having several utility functions for controlling the interval-effect:
- - `clearTimer` : `() => void` Cancels the current interval, preventing the handler from executing
- - `restartTimer` : `() => void` Clears the current timer and starts a new one. Optionally accepts a new interval value
+| Property | Type | Description |
+| ------------ | ------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
+| clearTimer | () => void | Clears the current interval timer, stopping the recurring execution of the handler. Similar to calling `clearInterval()` on a standard interval. |
+| restartTimer | (new_interval?: number) => void | Clears the current timer and starts a new one. Optionally accepts a `new_interval` parameter to use a different interval duration. If no interval is provided, uses the original interval value. |
-## Usage
+## Usage Examples
### Basic example
@@ -54,9 +55,3 @@ export default function Counter() {
)
}
```
-
-## Common Use Cases
-
-- Countdown timers
-- Real-time updates (clocks, progress bars)
-- Polling APIs at regular intervals
diff --git a/apps/doc/hooks/use-multi-intersection-observer.md b/apps/doc/hooks/use-multi-intersection-observer.md
index 89bb158..1ab025b 100644
--- a/apps/doc/hooks/use-multi-intersection-observer.md
+++ b/apps/doc/hooks/use-multi-intersection-observer.md
@@ -4,77 +4,48 @@ outline: deep
# use-multiple-intersection-observer
-A React hook that provides a convenient way to observe multiple elements simultaneously using the Intersection Observer API. Built on top of `useIntersectionObserver` for consistent behavior and TypeScript support.
+A React hook that provides a convenient way to observe multiple elements simultaneously using the [Intersection Observer](https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API) API.
-::: warning
-This hook requires you to understand the working of `useIntersectionObserver` hook. As it is built on top it, so it will help you to better understand about features of this hook. Read here [useIntersectionObserver](use-intersection-observer.html)
+::: info
+Built on top of [useIntersectionObserver](use-intersection-observer.html) hook for type-safety and consistent behaviors.
:::
## Features
- **Multiple observers:** Create multiple intersection observers with a single hook call
- **Consistent API:** Each observer follows the same pattern as `useIntersectionObserver`
-- **Type-safe:** Full TypeScript support with proper typing for all observer instances
- **Shared configuration:** Apply the same options to all observers while maintaining individual keys
-- **Auto cleanup:** All observers are automatically cleaned up on unmount
-- **Performance optimized:** Each observer is independently managed for optimal performance
## Problem It Solves
-::: info Multiple Hook Instance Boilerplate
-**_Problem:_** Managing many intersection observers requires repetitive hook calls
+::: details Multiple Hook Instance Boilerplate
+**Problem:** Managing many intersection observers requires repetitive hook calls
```ts
-// ❌ Without your hook - repetitive and verbose
+// ❌ Without multi-observer hook - repetitive
const hero = useIntersectionObserver({ key: 'hero', threshold: 0.5 })
const about = useIntersectionObserver({ key: 'about', threshold: 0.5 })
const services = useIntersectionObserver({ key: 'services', threshold: 0.5 })
const contact = useIntersectionObserver({ key: 'contact', threshold: 0.5 })
```
-**_Solution:_** Single hook call for multiple observers
+**Solution:** Single hook call for multiple observers
```ts
-// ✅ With your hook - clean and DRY
+// ✅ With multi-observer hook
const sections = useMultipleIntersectionObserver(['hero', 'about', 'services', 'contact'], { threshold: 0.5 })
```
:::
-::: info Memory and Performance Optimization
-**_Problem:_** Managing lifecycle of multiple observers manually
-
-- Risk of memory leaks with multiple observer instances
-- Complex cleanup logic for dynamic observer sets
-
-**_Solution:_** Automated lifecycle management
-
-- Leverages the proven cleanup logic of the base hook
-- Efficient memory usage through shared configuration
- :::
-
-::: info Component Organization and Maintainability
-**_Problem:_** Managing many observer hooks clutters component logic
-
-- Multiple hook calls at component top level
-- Scattered observer logic throughout component
-- Difficult to understand observer relationships
-
-**_Solution:_** Clean, organized observer management
-
-- Single hook call consolidates all observer logic
-- Clear relationship between observed elements
-- Easier to reason about component behavior
- :::
+::: details Type Safety at Scale
+**Problem:** Maintaining type safety with multiple dynamically named properties
-::: info Type Safety at Scale
-**_Problem:_** Maintaining type safety with multiple dynamically named properties
-
-- Lost type inference when managing multiple observers manually
+- Loses type inference when managing multiple observers manually
- No IntelliSense for dynamically generated property names
- Runtime errors from typos in property access
-**_Solution:_** Full type safety across all observers
+**Solution:** Full type safety across all observers
```ts
// ✅ Full type safety and IntelliSense
@@ -85,28 +56,16 @@ const observers = useMultipleIntersectionObserver(['hero', 'footer'] as const)
:::
-::: tip
-For better performance with many elements, consider grouping related observations or using a single observer with multiple targets if the behavior is identical.
-:::
-
## Parameters
-| Parameter | Type | Required | Default Value | Description |
-| --------- | :-------------------------------: | :------: | :-----------: | ------------------------------------------------- |
-| keys | `readonly Key[]` | ✅ | - | Array of unique keys for creating named observers |
-| options | [MultipleObserverOptions](#types) | ❌ | undefined | Shared configuration for all observers |
+| Parameter | Type | Required | Default Value | Description |
+| --------- | :------------------------------------------: | :------: | :-----------: | ------------------------------------------------- |
+| keys | readonly Key[] | ✅ | - | Array of unique keys for creating named observers |
+| options | [MultipleObserverOptions](#type-definitions) | ❌ | undefined | Shared configuration for all observers |
### Options Parameter
-All options from `useIntersectionObserver` except `key` (which is provided via the `keys` array):
-
-| Property | Type | Default | Description |
-| ----------------- | :------------------------------------------: | :---------: | ---------------------------------------------- |
-| `onIntersection` | `(entry: IntersectionObserverEntry) => void` | `undefined` | Callback fired on intersection changes |
-| `onlyTriggerOnce` | `boolean` | `false` | Whether to observe only the first intersection |
-| `root` | `Element \| Document \| null` | `null` | Root element for intersection |
-| `rootMargin` | `string` | `'0px'` | Margin around root element |
-| `threshold` | `number \| number[]` | `0` | Intersection ratio threshold(s) |
+All options from [useIntersectionObserver](use-intersection-observer.html) except `key` (which is provided via the `keys` array):
### Type Definitions
@@ -150,16 +109,20 @@ The hook returns a record object where each key from the input array maps to its
**`is{Capitalize}ElementIntersecting`:** Holds the boolean intersection status of the `element` weather it is intersecting the screen or not.
:::
+## Common Use Cases
+
+- **Multi-section navigation:** Track visibility of multiple page sections for active navigation states
+- **Lazy loading galleries:** Load multiple images or content blocks as they come into view
+
## Usage Examples
### Basic Multiple Observers
-```tsx {4-8,14,17,23,27,30,36,40,43,49}
+```tsx {4-9}
import { useMultipleIntersectionObserver } from 'classic-react-hooks'
export default function MultipleObserversExample() {
const observers = useMultipleIntersectionObserver(['header', 'main', 'footer'] as const, {
- // [!code ++]
threshold: 0.3,
onIntersection: (entry) => {
console.log('Element intersection changed:', entry.target.id)
@@ -170,41 +133,35 @@ export default function MultipleObserversExample() {
+ {hasBeenSeen ? 'I was seen!' : 'Scroll down to see me'}
+
)
@@ -303,12 +207,3 @@ export default function NavigationTracker() {
```
:::
-
-## Common Use Cases
-
-- **Multi-section navigation:** Track visibility of multiple page sections for active navigation states
-- **Lazy loading galleries:** Load multiple images or content blocks as they come into view
-- **Analytics tracking:** Monitor user engagement across different content areas
-- **Animation choreography:** Coordinate animations across multiple elements
-- **Performance monitoring:** Track which sections users actually view
-- **Infinite scroll sections:** Manage multiple loading zones in complex layouts
diff --git a/apps/doc/hooks/use-on-mount-effect.md b/apps/doc/hooks/use-on-mount-effect.md
index 01e1fab..924d3c5 100644
--- a/apps/doc/hooks/use-on-mount-effect.md
+++ b/apps/doc/hooks/use-on-mount-effect.md
@@ -6,13 +6,26 @@ outline: deep
A React hook that executes a callback function only once after the component mounts. This is a simplified wrapper around useEffect with an empty dependency array.
+::: tip
This hook is perfect for initialization logic that should run exactly once when a component first renders.
+:::
+
+## Features
+
+- **One-time execution:** Runs callback only once after component mounts
+- **Mount-only focus:** Explicitly designed for mount-time operations
+- **Compatible API:** Wrapper around `useEffect` hook
## Parameters
-| Parameter | Type | Required | Default Value | Description |
-| --------- | :------------------: | :------: | :-----------: | ----------------------------------------------------------------------------- |
-| cb | React.EffectCallback | ✅ | - | Callback function to execute once after mount. Can return a cleanup function. |
+| Parameter | Type | Required | Default Value | Description |
+| --------- | :------------------: | :------: | :-----------: | ------------------------------------------------------------------------------------------------------- |
+| cb | React.EffectCallback | ✅ | - | The callback function to execute once after component mounts. Can optionally return a cleanup function. |
+
+## Common Use Cases
+
+- **Setup Initialization:** Running one-time setup code
+- **Third-party libraries:** Initializing external libraries or plugins
## Usage Examples
@@ -39,8 +52,3 @@ export default function YourComponent() {
| Dependency mistakes | ⚠️ Easy to forget [] | ✅ No dependencies needed |
| TypeScript support | ✅ Yes | ✅ Yes |
| Cleanup support | ✅ Yes | ✅ Yes |
-
-## Common Use Cases
-
-- Setting up initial state or configuration
-- Any one-time setup that shouldn't repeat after component mount
diff --git a/apps/doc/hooks/use-outside-click.md b/apps/doc/hooks/use-outside-click.md
index 75ceb56..09fc0dc 100644
--- a/apps/doc/hooks/use-outside-click.md
+++ b/apps/doc/hooks/use-outside-click.md
@@ -6,39 +6,71 @@ outline: deep
A React hook that detects outside click for specified element and triggers the given callback.
+::: tip
Perfect for implementing modals, dropdowns and other UI components that need to be closed when users click outside of them.
+:::
## Features
- **Precise trigger:** Precise outside click detection
- **Performance:** Optimized with capture phase events
-- **Underlying hook:** At its core, it uses `useEventListener` hook
+- **Underlying hook:** At its core, it uses [useEventListener](use-event-listener.html) hook
## Parameters
-| Parameter | Type | Required | Default Value | Description |
-| --------- | :-----------------: | :------: | :-----------: | ------------------------------------------------ |
-| target | [EvTarget](#types) | ✅ | - | Function that returns the target element or null |
-| handler | [EvHandler](#types) | ❌ | undefined | Callback executed on outside click |
-| options | [EvOptions](#types) | ❌ | undefined | Event listener options and feature flags |
+| Parameter | Type | Required | Default Value | Description |
+| --------- | :----------------------------: | :------: | :-----------: | ------------------------------------------------ |
+| target | [EvTarget](#type-definitions) | ✅ | - | Function that returns the target element or null |
+| handler | [EvHandler](#type-definitions) | ❌ | undefined | Callback executed on outside click |
+| options | [EvOptions](#type-definitions) | ❌ | undefined | Event listener options and feature flags |
-### Types
+#### Standard AddEventListenerOptions
-```ts
+| Property | Type | Default | Description |
+| -------- | ----------- | --------- | -------------------------------------------------------------------------------- |
+| capture | boolean | false | If `true`, the listener will be triggered during the capture phase |
+| once | boolean | false | If `true`, the listener will be automatically removed after being triggered once |
+| passive | boolean | false | If `true`, indicates that the function will never call `preventDefault()` |
+| signal | AbortSignal | undefined | An AbortSignal that can be used to remove the event listener |
+
+#### Custom Options
+
+| Property | Type | Default | Description |
+| ----------------- | -------------- | ------- | --------------------------------------------------------------------------------------------------- |
+| shouldInjectEvent | boolean \| any | true | Controls whether the event listener should be attached. When false, the event listener is not added |
+
+### Type Definitions
+
+::: details
+
+```tsx
type EvTarget = () => EventTarget | null
type EvHandler = (event: DocumentEventMap['click']) => void
+
interface EvOptions extends AddEventListenerOptions {
// Standard AddEventListenerOptions:
- // capture?: boolean
- // once?: boolean
- // passive?: boolean
- // signal?: AbortSignal
+ capture?: boolean
+ once?: boolean
+ passive?: boolean
+ signal?: AbortSignal
// Custom option:
shouldInjectEvent?: boolean | any // Controls whether the event should be attached
}
```
+:::
+
+## Return Value(s)
+
+This hook does not return anything.
+
+## Common Use Cases
+
+- Modal dialogs - Close when clicking backdrop
+- Dropdown menus - Hide when clicking elsewhere
+- Context menus - Dismiss on outside click
+
## Usage Examples
### Modal Component
@@ -72,9 +104,11 @@ function Modal() {
}
```
-#### Conditional Outside Click
+### Conditional Outside Click
-```ts
+::: details Example
+
+```ts {10-12}
function ConditionalOutsideClick() {
const [isModalOpen, setIsModalOpen] = useState(false)
const [isPinned, setIsPinned] = useState(false)
@@ -103,9 +137,13 @@ function ConditionalOutsideClick() {
}
```
-#### Dynamic Target
+:::
-```ts
+### Dynamic Target
+
+::: details Example
+
+```ts {5,12-14,18}
function DynamicTarget() {
const [activeElement, setActiveElement] = useState(null)
@@ -131,8 +169,4 @@ function DynamicTarget() {
}
```
-## Common Use Cases
-
-- Modal dialogs - Close when clicking backdrop
-- Dropdown menus - Hide when clicking elsewhere
-- Context menus - Dismiss on outside click
+:::
diff --git a/apps/doc/hooks/use-synced-effect.md b/apps/doc/hooks/use-synced-effect.md
index 8c483c0..80ef2a0 100644
--- a/apps/doc/hooks/use-synced-effect.md
+++ b/apps/doc/hooks/use-synced-effect.md
@@ -6,21 +6,27 @@ outline: deep
A React hook that executes a callback when dependencies change, similar to `useEffect`, but skips execution on the initial mount.
+::: tip
This is particularly useful when you want to respond to state changes without triggering side effects during the component's first render.
+:::
## Features
- **Skip initial mount:** Skipping the callback on initial mount
- **Reactive:** Running the callback only when dependencies actually change
- **React StrictMode:** Handling React StrictMode double execution correctly
-- **Flexible:** Supporting cleanup functions just like useEffect
+- **Flexible control:** Supporting cleanup functions just like useEffect
## Parameters
-| Parameter | Type | Required | Default Value | Description |
-| --------- | :------------------: | :------: | :-----------: | ------------------------------------------------------------------------------------ |
-| cb | React.EffectCallback | ✅ | - | Callback function to execute when dependencies change. Can return a cleanup function |
-| deps | React.DependencyList | ❌ | [] | Array of dependencies to watch for changes |
+| Parameter | Type | Required | Default Value | Description |
+| --------- | :------------------: | :------: | :-----------: | --------------------------------------------------------------------------------------------------- |
+| cb | React.EffectCallback | ✅ | - | The callback function to execute when dependencies change. Can optionally return a cleanup function |
+| deps | React.DependencyList | ❌ | [] | An array of dependencies that the effect depends on. |
+
+## Common Use Cases
+
+- Use everywhere just like `useEffect`
## Usage Examples
@@ -45,9 +51,11 @@ export default function YourComponent() {
}
```
-### With Cleanup Function
+### Same cleanup behaviour just like `useEffect`
-```ts
+::: details Example
+
+```ts {21-23}
import { useState } from 'react'
import { useSyncedEffect } from 'classic-react-hooks'
@@ -78,6 +86,8 @@ function SearchComponent() {
}
```
+:::
+
## Comparison with useEffect
| Scenario | useEffect | useSyncedEffect |
@@ -86,11 +96,3 @@ function SearchComponent() {
| Dependency changes | ✅ Runs | ✅ Runs |
| Cleanup support | ✅ Yes | ✅ Yes |
| StrictMode handling | ⚠️ May run twice | ✅ Handles correctly |
-
-## Important notes
-
-- Empty dependency array [] means the effect will never run (since there are no dependencies to change)
-
-## Common Use Cases
-
-- Use everywhere just like `useEffect`
diff --git a/apps/doc/hooks/use-synced-ref.md b/apps/doc/hooks/use-synced-ref.md
index ee489af..5ce6f77 100644
--- a/apps/doc/hooks/use-synced-ref.md
+++ b/apps/doc/hooks/use-synced-ref.md
@@ -4,29 +4,106 @@ outline: deep
# use-synced-ref
-A React hook that creates a ref that automatically stays in sync with the provided value.
-
-This eliminates the need to manually update refs and helps avoid stale closure issues in callbacks and effects.
+A React hook that creates a ref that automatically stays in sync with the provided value, ensuring you always have access to the latest state in asynchronous operations.
## Features
-- **Reactive:** Automatic synchronization with any value
-- **Prevent State Closure:** Prevents stale closure problems
-- **No Re-render:** Zero re-renders - purely ref-based
+- **Always current:** Ref automatically updates to reflect the latest value
+- **Stale closure prevention:** Prevents accessing stale values in async operations
+- **Simple API:** Minimal interface with automatic synchronization
+- **Type safe:** Full TypeScript support with generic typing
+
+## Problem It Solves
+
+::: details **Stale Closure Problem**
+
+**Problem:** In React, when you capture state values in closures (like in `setTimeout`, event handlers, or async operations), you might get stale values due to how JavaScript closures work.
+
+```tsx
+// ❌ Problematic approach - stale closure issue
+function Component() {
+ const [count, setCount] = useState(0)
+
+ const handleAsyncOperation = () => {
+ setTimeout(() => {
+ // This will always log the value of count when handleAsyncOperation was called
+ // Not the current value after 2 seconds
+ console.log('Stale count:', count) // Might be outdated!
+ }, 2000)
+ }
+
+ return (
+
+
Count: {count}
+
+
+
+ )
+}
+```
+
+**Solution:**
+
+- Provides a ref that always contains the current value
+- Eliminates stale closure issues in async operations
+- Ensures you can access the latest state regardless of when the closure was created
+
+```tsx
+// ✅ Clean approach with useSyncedRef
+function Component() {
+ const [count, setCount] = useState(0)
+ const countRef = useSyncedRef(count)
+
+ const handleAsyncOperation = () => {
+ setTimeout(() => {
+ // countRef.current always has the latest value
+ console.log('Current count:', countRef.current) // Always up-to-date!
+ }, 2000)
+ }
+
+ return (
+
+
Count: {count}
+
+
+
+ )
+}
+```
+
+:::
+
+::: details **Performance Benefits**
+
+- Lightweight solution with minimal overhead
+- No unnecessary re-renders or effect dependencies
+- Simple ref assignment on every render ensures synchronization
+
+:::
## Parameters
-| Parameter | Type | Required | Default Value | Description |
-| --------- | :--: | :------: | :-----------: | ------------------------------------------------- |
-| value | any | ✅ | - | Any value to be tracked and kept in sync with ref |
+| Parameter | Type | Required | Default Value | Description |
+| --------- | :--: | :------: | :-----------: | ------------------------------------------- |
+| state | T | ✅ | - | The value to keep synchronized with the ref |
-## Returns
+## Return Value(s)
+
+This hook returns the ref version of the provided state, which auto syncs up
+
+| Return Value | Type | Description |
+| ------------ | ------------- | ------------------------------------------------------------------------------------- |
+| syncedRef | RefObject | A mutable ref object whose `.current` property always contains the latest state value |
+
+## Common Use Cases
-- Returns a `React.MutableRefObject` that always contains the latest value of the provided state.
+- **Async operations:** Accessing latest state in `setTimeout`, `setInterval`, or API calls
+- **Event handlers:** Ensuring event callbacks have access to current state without stale closures
+- **WebSocket handlers:** Accessing current state in WebSocket message handlers
-## Usage
+## Usage Examples
-### Basic Example
+### Basic State Synchronization
```ts
import { useState } from 'react'
@@ -54,51 +131,44 @@ export default function Counter() {
}
```
-## Problem It Solves
+### Event Handlers with Latest State
-### The Stale Closure Problem
+::: details Example
-In React, when you use hooks like useEffect, useCallback, or setTimeout with dependency arrays, you often encounter stale closure issues:
+```ts {6,11}
+import { useState, useCallback } from 'react'
+import { useSyncedRef } from 'classic-react-hooks'
-```ts
-// ❌ Problematic code
-function ProblematicComponent() {
- const [count, setCount] = useState(0)
+export default function EventExample() {
+ const [messages, setMessages] = useState([])
+ const messagesRef = useSyncedRef(messages)
- useEffect(() => {
- const interval = setInterval(() => {
- console.log(count) // Always logs 0 (stale closure)
- }, 1000)
- return () => clearInterval(interval)
- }, []) // Empty deps = stale closure
+ const handleKeyPress = useCallback((event: KeyboardEvent) => {
+ if (event.key === 'Enter') {
+ // Always access the latest messages array
+ const currentMessages = messagesRef.current
+ console.log('Current messages count:', currentMessages.length)
- // vs
+ setMessages((prev) => [...prev, `Message ${prev.length + 1}`])
+ }
+ }, []) // No need to include messages in dependencies
useEffect(() => {
- const interval = setInterval(() => {
- console.log(count) // Works but recreates interval on every count change
- }, 1000)
- return () => clearInterval(interval)
- }, [count]) // Including count fixes staleness but causes recreation
-}
-
-// ✅ Solution with useSyncedRef
-function SolvedComponent() {
- const [count, setCount] = useState(0)
- const countRef = useSyncedRef(count)
+ document.addEventListener('keydown', handleKeyPress)
+ return () => document.removeEventListener('keydown', handleKeyPress)
+ }, [handleKeyPress])
- useEffect(() => {
- const interval = setInterval(() => {
- console.log(countRef.current) // Always logs latest count
- }, 1000)
- return () => clearInterval(interval)
- }, []) // Empty deps = no recreation, no staleness!
+ return (
+
+
Press Enter to add messages
+
+ {messages.map((msg, index) => (
+
{msg}
+ ))}
+
+
+ )
}
```
-## Common Use Cases
-
-- Accessing latest state in intervals/timeouts
-- Event handlers that need current state
-- Custom hooks with complex state dependencies
-- Preventing effect recreations while avoiding stale closures
+:::
diff --git a/apps/doc/hooks/use-throttled-fn.md b/apps/doc/hooks/use-throttled-fn.md
index 2241208..9ec92c9 100644
--- a/apps/doc/hooks/use-throttled-fn.md
+++ b/apps/doc/hooks/use-throttled-fn.md
@@ -4,97 +4,240 @@ outline: deep
# use-throttled-fn
-A React hook that returns a throttled version of a callback function.
+A React hook that returns a throttled version of provided function, ensuring it executes at most once per specified time interval, regardless of how frequently it's called.
-Throttling ensures that the function is called at most once per specified time interval, regardless of how many times it's invoked.
+## Features
-This is particularly useful for performance optimization in scenarios like handling rapid user input, scroll events, or API calls.
+- **Rate limiting:** Ensures function executes at most once per specified interval
+- **Immediate execution:** First call executes immediately, subsequent calls are throttled
+- **Performance optimized:** Prevents excessive function calls during rapid user interactions
+- **Context preservation:** Maintains original function's `this` context and error behavior
+- **Error handling:** Preserves original function's error behavior
-## Features
+## Problem It Solves
+
+::: details **Boilerplate Reduction**
+
+**Problem:** Manually implementing throttling in React components becomes lengthy, little bit complex code with potential performance issues and inconsistent behavior.
+
+```tsx
+// ❌ Problematic approach which is redundant and verbose
+function ScrollTracker() {
+ const [scrollPosition, setScrollPosition] = useState(0)
+ const lastExecutionTimeRef = useRef(0)
+ const THROTTLE_DELAY = 100
+
+ const handleScroll = useCallback(() => {
+ const currentTime = Date.now()
+
+ if (currentTime - lastExecutionTimeRef.current >= THROTTLE_DELAY) {
+ setScrollPosition(window.scrollY)
+ lastExecutionTimeRef.current = currentTime
+
+ // Additional complex logic for tracking scroll performance
+ console.log('Scroll position updated:', window.scrollY)
+ }
+ }, [])
+
+ useEffect(() => {
+ const throttledScrollHandler = (event: Event) => {
+ handleScroll()
+ }
+
+ window.addEventListener('scroll', throttledScrollHandler, { passive: true })
+
+ return () => {
+ window.removeEventListener('scroll', throttledScrollHandler)
+ }
+ }, [handleScroll])
+
+ return
+}
+```
+
+:::
+
+::: details **Performance Benefits**
-- **Throttling Functionality:** Limits function execution to at most once per specified time period
-- **Configurable Delay:** Accepts a custom delay period with a default of 300ms
-- **Dynamic Props Updates:** The hook properly handles updates to both the callback function and delay value during re-renders without losing the debouncing behavior
-- **Performance optimized:** Prevents excessive function calls
+- **Reduces execution frequency:** Limits function calls to specified intervals, preventing performance bottlenecks
+- **Memory efficient:** Minimal overhead with simple timestamp tracking
+
+:::
+
+## Throttling vs Debouncing
+
+| Aspect | Throttling | Debouncing |
+| -------------------- | ---------------------------------- | ------------------------------------------ |
+| **Execution timing** | Executes at regular intervals | Executes after inactivity period |
+| **First call** | Executes immediately | May delay first execution |
+| **Use case** | Continuous events (scroll, resize) | User input completion (search, validation) |
+| **Frequency** | Limited to X times per interval | Executes only after pause |
## Parameters
-| Parameter | Type | Required | Default Value | Description |
-| ------------------ | :------: | :------: | :-----------: | --------------------------------------------------------- |
-| callbackToThrottle | Function | ✅ | - | The callback function that should be throttled |
-| delay | number | ❌ | 300 | Delay in milliseconds between allowed function executions |
+| Parameter | Type | Required | Default Value | Description |
+| ------------------ | :------------------------------: | :------: | :-----------: | ----------------------------------------------------------------- |
+| callbackToThrottle | [ThrottledFn](#type-definitions) | ✅ | - | The function to throttle |
+| delay | number | ❌ | 300ms | Minimum time interval between function executions in milliseconds |
+
+### Type Definitions
+
+::: details
+
+```ts
+export type ThrottledFn any> = (...args: Parameters) => void
+```
+
+:::
-## Returns
+## Return Value(s)
-- Returns a throttled version of the provided callback function that maintains the same signature and behavior, but with throttling applied.
+This hook returns the throttled version of provided callback.
+
+| Return Value | Type | Description |
+| ------------- | ---------------------------------- | ------------------------------------------------------------------------------------------ |
+| `throttledFn` | `(...args: Parameters) => void` | Throttled version of the original function that limits execution to the specified interval |
+
+## Common Use Cases
+
+- **API Rate Limiting:** Limiting search API calls while typing
+- **Mouse Movement Tracking:** Tracking mouse coordinate and update on screen
+- **Real-time data updates:** Managing WebSocket or polling frequency
## Usage Examples
-### Basic API throttling
+### API Rate Limiting
-```ts
+```tsx {9-25}
import { useState } from 'react'
import { useThrottledFn } from 'classic-react-hooks'
-export default function AutoSave() {
- const [content, setContent] = useState('')
- const [saving, setSaving] = useState(false)
+export default function ApiExample() {
+ const [data, setData] = useState([])
+ const [requestCount, setRequestCount] = useState(0)
+ const [isLoading, setIsLoading] = useState(false)
+
+ const throttledFetch = useThrottledFn({
+ callbackToThrottle: async () => {
+ setIsLoading(true)
+ setRequestCount((prev) => prev + 1)
- const saveContent = useThrottledFn({
- callbackToThrottle: async (text) => {
- setSaving(true)
try {
- await saveToAPI(text)
- console.log('Content saved!')
+ const response = await fetch('https://jsonplaceholder.typicode.com/posts?_limit=5')
+ const result = await response.json()
+ setData(result)
} catch (error) {
- console.error('Save failed:', error)
+ console.error('Fetch failed:', error)
} finally {
- setSaving(false)
+ setIsLoading(false)
}
},
- delay: 2000, // Auto-save every 2 seconds at most
+ delay: 2000, // Max 1 request per 2 seconds
})
- const handleChange = (e) => {
- const newContent = e.target.value
- setContent(newContent)
- saveContent(newContent)
- }
-
return (
-
-
- {saving &&
Saving...
}
+
+
+
API calls made: {requestCount}
+
+ {data.map((item) => (
+
+ {item.title}
+
+ ))}
+
+
+ Click rapidly - API calls are throttled to 1 per 2 seconds
+
)
}
```
-## Common Use Cases
+### Mouse Movement Tracking
-- Real-time form validation
-- Prevent excessive API calls
-- Throttle scroll event processing
+::: details Example
-## Alternative: Non-React Usage
+```tsx {8-17}
+import { useState, useEffect } from 'react'
+import { useThrottledFn } from 'classic-react-hooks'
-For use outside of React components, use the standalone wrapper:
+export default function MouseTracker() {
+ const [mousePos, setMousePos] = useState({ x: 0, y: 0 })
+ const [updateCount, setUpdateCount] = useState(0)
+
+ const throttledMouseMove = useThrottledFn({
+ callbackToThrottle: (event: MouseEvent) => {
+ setMousePos({
+ x: event.clientX,
+ y: event.clientY,
+ })
+ setUpdateCount((prev) => prev + 1)
+ },
+ delay: 50, // 20 updates per second max
+ })
-```ts
-import { throttledFnWrapper } from 'classic-react-hooks'
-
-const throttledLog = throttledFnWrapper({
- callbackToThrottle: (message: string) => console.log(message),
- delay: 1000,
-})
-
-// Use the throttled function
-throttledLog('Hello') // will log "Hello" in console
-throttledLog('World') // ignored till 1 second is not passed
-throttledLog('1') // ignored till 1 second is not passed
-throttledLog('2') // ignored till 1 second is not passed
-throttledLog('3') // ignored till 1 second is not passed
-// block for 1 second
-await sleep(1000)
-throttledLog('4') // will log "4" in console
+ useEffect(() => {
+ const handleMouseMove = (event: MouseEvent) => {
+ throttledMouseMove(event)
+ }
+
+ document.addEventListener('mousemove', handleMouseMove)
+ return () => document.removeEventListener('mousemove', handleMouseMove)
+ }, [throttledMouseMove])
+
+ return (
+
+
Mouse Position Tracker
+
+ Position: ({mousePos.x}, {mousePos.y})
+
+
Updates: {updateCount}
+
+ Move your mouse around this area
+
+
+ )
+}
```
+
+:::
diff --git a/apps/doc/hooks/use-timeout-effect.md b/apps/doc/hooks/use-timeout-effect.md
index 0ceddae..df1902b 100644
--- a/apps/doc/hooks/use-timeout-effect.md
+++ b/apps/doc/hooks/use-timeout-effect.md
@@ -9,9 +9,8 @@ A React hook that fires a provided callback after a specified timeout, similar t
## Features
- **Scheduled execution:** Executes a callback after a specified delay
-- **Flexible:** Provides methods to clear or restart the timer
-- **Automatic Cleanup:** Automatically cleans up timer on component unmount
-- **Syncronization:** Syncs with the latest callback and timeout values
+- **Flexible control:** Provides methods to clear or restart the timer
+- **Auto cleanup:** Automatically clears up timer on component unmount
## Parameters
@@ -20,12 +19,14 @@ A React hook that fires a provided callback after a specified timeout, similar t
| handler | Function | ✅ | - | The callback function to execute after the timeout |
| timeout | number | ❌ | 100 | The delay in milliseconds before executing the handler |
-## Returns
+## Return value(s)
-- Returns an object with control methods:
+This hooks returns an object having several utility functions for controlling the timeout-effect:
- - `clearTimer` : `() => void` Cancels the current timeout, preventing the handler from executing
- - `restartTimer` : `() => void` Clears the current timer and starts a new one. Optionally accepts a new timeout value
+| Property | Type | Description |
+| ------------ | ------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| clearTimer | () => void | Clears the current timeout timer, preventing the handler from executing if it hasn't already run. Similar to calling `clearTimeout()` on a standard timeout. |
+| restartTimer | (new_interval?: number) => void | Clears the current timer and starts a new one. Optionally accepts a `new_interval` parameter to use a different timeout duration. If no interval is provided, uses the original timeout value. |
## Usage Examples
@@ -48,8 +49,3 @@ export default function BasicExample() {
return
{message}
}
```
-
-## Important Notes
-
-- The hook uses `useSyncedRef` to ensure the latest callback and timeout values are always used.
-- The restartTimer method can accept an optional new timeout value, otherwise it uses the original timeout.
diff --git a/apps/doc/hooks/use-window-resize.md b/apps/doc/hooks/use-window-resize.md
index f8377e7..d4fc48e 100644
--- a/apps/doc/hooks/use-window-resize.md
+++ b/apps/doc/hooks/use-window-resize.md
@@ -4,41 +4,68 @@ outline: deep
# use-window-resize
-A React hook that evaluates a callback function on window resize events and returns the result.
+A React hook that evaluates provided callback function on window resize event and returns the result of it.
+::: tip
Perfect for responsive behavior based on window dimensions.
+:::
+
+::: info
+At it's core, [useEventListener](use-event-listener.html) hook is used internally for listening to resize event.
+:::
## Features
- **Custom handler:** Execute custom logic on window resize
- **Reactive:** Automatic re-evaluation and state updates
- **Configurable:** Configurable default values and event injection
-- **Underlying hook:** At its core, it uses `useEventListener` hook
+- **Performance:** It uses [useLayoutEffect](https://react.dev/reference/react/useLayoutEffect) hook for resize event listening
## Parameters
-| Parameter | Type | Required | Default Value | Description |
-| --------- | :---------------: | :------: | :-----------: | ------------------------------------------- |
-| handler | [Handler](#types) | ✅ | - | Callback function executed on window resize |
-| options | [Options](#types) | ❌ | undefined | Configuration options |
+| Parameter | Type | Required | Default Value | Description |
+| --------- | :--------------------------: | :------: | :-----------: | ------------------------------------------- |
+| handler | [Handler](#type-definitions) | ✅ | - | Callback function executed on window resize |
+| options | [Options](#type-definitions) | ❌ | undefined | Configuration options |
| |
-### Types
+### Options Parameter
+
+| Property | Type | Default | Description |
+| ----------------- | ----------- | --------- | ----------------------------------------------------------------------------------------------------------------------- |
+| defaultValue | Generic `T` | undefined | The initial value returned before the first resize event. If not provided, the `handler` is called immediately on mount |
+| shouldInjectEvent | boolean | true | Controls whether the resize event listener should be attached. When `false`, the listener is not added |
+
+### Type Definitions
+
+::: details
```ts
type Handler = () => T
type Options = { shouldInjectEvent?: boolean; defaultValue?: T }
```
-## Returns
+:::
+
+## Return value(s)
-- Returns the current result of the `handler` function, updated whenever the window is resized.
+Returns the evaluated result of the `handler` function call on resize event.
+
+| Return Value | Type | Description |
+| --------------------------------- | ----------- | -------------------------- |
+| Result of `handler` function call | Generic `T` | Result to use in component |
+
+## Common Use Cases
+
+- Creating dynamic layouts
+- Toggling element visibility based on window dimension
+- Dynamically lazy loading components for mobile and desktop screens
## Usage Examples
### Basic Responsive Breakpoints
-```ts
+```ts {4-10,15-17}
import { useWindowResize } from 'classic-react-hooks'
function ResponsiveComponent() {
@@ -61,36 +88,16 @@ function ResponsiveComponent() {
}
```
-#### Window Dimensions Tracking
-
-```ts
-import { useWindowResize } from 'classic-react-hooks'
-
-function WindowDimensions() {
- const dimensions = useWindowResize(() => ({
- width: window.innerWidth,
- height: window.innerHeight,
- aspectRatio: window.innerWidth / window.innerHeight,
- }))
+### With Default Value
- return (
-
)
}
- *
+ *
* @see Docs https://classic-react-hooks.vercel.app/hooks/use-debounced-fn.html
*
*/
@@ -87,7 +86,7 @@ export default function useDebouncedFn any>({
/**
* @description
* A React hook that returns a debounced version of any function, delaying its execution until after a specified delay has passed since the last time it was invoked.
- *
+ *
* @example
import { useState, useEffect } from 'react'
import { useDebouncedFn } from 'classic-react-hooks'
@@ -132,7 +131,7 @@ export default function useDebouncedFn any>({
)
}
- *
+ *
* @see Docs https://classic-react-hooks.vercel.app/hooks/use-debounced-fn.html
*/
export function debouncedFnWrapper any>(props: { callbackToBounce: T; delay?: number }) {
diff --git a/src/lib/use-event-listener/index.tsx b/src/lib/use-event-listener/index.tsx
index 2675a15..791dfc4 100644
--- a/src/lib/use-event-listener/index.tsx
+++ b/src/lib/use-event-listener/index.tsx
@@ -1,4 +1,3 @@
-'use client'
import type { EvHandler, EvOptions, EvTarget } from '../../types'
import React, { useEffect, useLayoutEffect, useRef, useState } from 'react'
@@ -15,7 +14,6 @@ export type UseEventListenerReturnValues = {
* A React hook that provides a declarative way to add DOM event listeners with automatic cleanup.
*
* @example
- import { useRef } from 'react'
import { useEventListener } from 'classic-react-hooks'
export default function ClickExample() {
diff --git a/src/lib/use-interval-effect/index.tsx b/src/lib/use-interval-effect/index.tsx
index 4224563..df2d6e8 100644
--- a/src/lib/use-interval-effect/index.tsx
+++ b/src/lib/use-interval-effect/index.tsx
@@ -1,11 +1,10 @@
-'use client'
import React, { useEffect, useRef } from 'react'
import useSyncedRef from '../use-synced-ref'
/**
* @description
* A React hook that executes a callback function at regular intervals, similar to `setInterval` but with additional control methods for clearing and restarting the timer.
- *
+ *
* @example
import { useState } from 'react'
import { useIntervalEffect } from 'classic-react-hooks'
@@ -28,7 +27,7 @@ import useSyncedRef from '../use-synced-ref'
)
}
- *
+ *
* @see Docs https://classic-react-hooks.vercel.app/hooks/use-interval-effect.html
*/
export default function useIntervalEffect({ handler, interval = 100 }: { handler: () => void; interval?: number }) {
diff --git a/src/lib/use-local-storage/index.tsx b/src/lib/use-local-storage/index.tsx
index fa52c33..6d8b26d 100644
--- a/src/lib/use-local-storage/index.tsx
+++ b/src/lib/use-local-storage/index.tsx
@@ -1,4 +1,3 @@
-'use client'
import type { Dispatch, SetStateAction } from 'react'
import React, { useRef, useCallback, useSyncExternalStore } from 'react'
diff --git a/src/lib/use-multi-intersection-observer/index.tsx b/src/lib/use-multi-intersection-observer/index.tsx
index 9959edd..caff80c 100644
--- a/src/lib/use-multi-intersection-observer/index.tsx
+++ b/src/lib/use-multi-intersection-observer/index.tsx
@@ -2,7 +2,7 @@ import type { IntersectionObserverOptions } from '../use-intersection-observer'
import useIntersectionObserver from '../use-intersection-observer'
-export default function useMultipleIntersectionObserver(
+export default function useMultiIntersectionObserver(
keys: readonly Key[],
options?: Omit
) {
diff --git a/src/lib/use-on-mount-effect/index.tsx b/src/lib/use-on-mount-effect/index.tsx
index 0f1c1a4..cc29edb 100644
--- a/src/lib/use-on-mount-effect/index.tsx
+++ b/src/lib/use-on-mount-effect/index.tsx
@@ -1,4 +1,3 @@
-'use client'
import type { EffectCallback } from 'react'
import React, { useEffect } from 'react'
diff --git a/src/lib/use-outside-click/index.tsx b/src/lib/use-outside-click/index.tsx
index 296556b..e4efa33 100644
--- a/src/lib/use-outside-click/index.tsx
+++ b/src/lib/use-outside-click/index.tsx
@@ -1,4 +1,3 @@
-'use client'
import type { EvOptions, EvTarget } from '../../types'
import React from 'react'
@@ -9,7 +8,7 @@ import { useEventListener } from '../use-event-listener'
* A React hook that detects outside click for specified element and triggers the given callback.
*
* @example
- import { useRef, useState } from 'react'
+ import { useState } from 'react'
import { useOutsideClick } from 'classic-react-hooks'
function Modal() {
diff --git a/src/lib/use-synced-effect/index.tsx b/src/lib/use-synced-effect/index.tsx
index 8801620..50a5b6d 100644
--- a/src/lib/use-synced-effect/index.tsx
+++ b/src/lib/use-synced-effect/index.tsx
@@ -1,4 +1,3 @@
-'use client'
import type { DependencyList, EffectCallback } from 'react'
import React, { useEffect, useRef } from 'react'
diff --git a/src/lib/use-synced-ref/index.tsx b/src/lib/use-synced-ref/index.tsx
index 8d0a969..6e2da76 100644
--- a/src/lib/use-synced-ref/index.tsx
+++ b/src/lib/use-synced-ref/index.tsx
@@ -1,4 +1,3 @@
-'use client'
import React, { useRef } from 'react'
/**
diff --git a/src/lib/use-throttled-fn/index.tsx b/src/lib/use-throttled-fn/index.tsx
index 3435747..fbd037f 100644
--- a/src/lib/use-throttled-fn/index.tsx
+++ b/src/lib/use-throttled-fn/index.tsx
@@ -1,4 +1,3 @@
-'use client'
import React, { useRef } from 'react'
const DEFAULT_DELAY = 300
@@ -7,7 +6,7 @@ const DEFAULT_DELAY = 300
* @description
* A React hook that returns a throttled version of a callback function.
*
- * @example
+ * @example
import { useState } from 'react'
import { useThrottledFn } from 'classic-react-hooks'
@@ -42,7 +41,7 @@ const DEFAULT_DELAY = 300
{saving &&