use-web-kit 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +176 -0
- package/dist/index.d.mts +23 -0
- package/dist/index.d.ts +23 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +2 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +70 -0
package/README.md
ADDED
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
# use-web-kit
|
|
2
|
+
|
|
3
|
+
**A zero-dependency, lightweight (~3KB) collection of React hooks optimized for performance and modern Browser APIs.**
|
|
4
|
+
|
|
5
|
+
`License: MIT` | `TypeScript: Included` | `Size: ~3KB`
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Features
|
|
10
|
+
|
|
11
|
+
- Full TypeScript support with exported types
|
|
12
|
+
- Dual ESM and CJS bundles
|
|
13
|
+
- Tree-shakeable exports
|
|
14
|
+
- Zero runtime dependencies
|
|
15
|
+
- Production-ready with comprehensive test coverage
|
|
16
|
+
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
## Installation
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
npm install use-web-kit
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
yarn add use-web-kit
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
pnpm add use-web-kit
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
---
|
|
34
|
+
|
|
35
|
+
## API
|
|
36
|
+
|
|
37
|
+
### useIdleQueue
|
|
38
|
+
|
|
39
|
+
Queues non-urgent tasks to run during browser idle time using `requestIdleCallback` with a robust `setTimeout` fallback for Safari and older browsers.
|
|
40
|
+
|
|
41
|
+
**Signature**
|
|
42
|
+
|
|
43
|
+
```ts
|
|
44
|
+
const { enqueue, clearQueue, queueLength } = useIdleQueue(options?)
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
**Options**
|
|
48
|
+
|
|
49
|
+
| Parameter | Type | Default | Description |
|
|
50
|
+
| ------------------ | -------- | ----------- | ------------------------------------------- |
|
|
51
|
+
| `timeout` | `number` | `undefined` | Max wait time (ms) before forcing execution |
|
|
52
|
+
| `fallbackInterval` | `number` | `50` | Interval (ms) for the `setTimeout` fallback |
|
|
53
|
+
|
|
54
|
+
**Usage**
|
|
55
|
+
|
|
56
|
+
```tsx
|
|
57
|
+
import { useIdleQueue } from "use-web-kit";
|
|
58
|
+
|
|
59
|
+
function Dashboard() {
|
|
60
|
+
const { enqueue, queueLength } = useIdleQueue({ timeout: 2000 });
|
|
61
|
+
|
|
62
|
+
const handleClick = () => {
|
|
63
|
+
enqueue(() => {
|
|
64
|
+
fetch("/api/analytics", {
|
|
65
|
+
method: "POST",
|
|
66
|
+
body: JSON.stringify({ event: "button_click" }),
|
|
67
|
+
});
|
|
68
|
+
});
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
return (
|
|
72
|
+
<div>
|
|
73
|
+
<button onClick={handleClick}>Track Event</button>
|
|
74
|
+
<span>Pending tasks: {queueLength}</span>
|
|
75
|
+
</div>
|
|
76
|
+
);
|
|
77
|
+
}
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
---
|
|
81
|
+
|
|
82
|
+
### useBroadcastState
|
|
83
|
+
|
|
84
|
+
Synchronizes state across different browser tabs and windows in real-time using the Broadcast Channel API. Works as a drop-in replacement for `useState`.
|
|
85
|
+
|
|
86
|
+
**Signature**
|
|
87
|
+
|
|
88
|
+
```ts
|
|
89
|
+
const [state, setState] = useBroadcastState<T>(channelName, initialValue);
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
| Parameter | Type | Description |
|
|
93
|
+
| -------------- | -------- | ----------------------------------------------- |
|
|
94
|
+
| `channelName` | `string` | Unique name identifying the shared channel |
|
|
95
|
+
| `initialValue` | `T` | Initial state value (must be JSON-serializable) |
|
|
96
|
+
|
|
97
|
+
**Usage**
|
|
98
|
+
|
|
99
|
+
```tsx
|
|
100
|
+
import { useBroadcastState } from "use-web-kit";
|
|
101
|
+
|
|
102
|
+
function Counter() {
|
|
103
|
+
const [count, setCount] = useBroadcastState<number>("shared-counter", 0);
|
|
104
|
+
|
|
105
|
+
return (
|
|
106
|
+
<div>
|
|
107
|
+
<p>Count: {count}</p>
|
|
108
|
+
<button onClick={() => setCount((prev) => prev + 1)}>Increment</button>
|
|
109
|
+
<button onClick={() => setCount(0)}>Reset</button>
|
|
110
|
+
</div>
|
|
111
|
+
);
|
|
112
|
+
}
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
Open this component in multiple tabs to see the count stay in sync.
|
|
116
|
+
|
|
117
|
+
---
|
|
118
|
+
|
|
119
|
+
### useAdaptivePolling
|
|
120
|
+
|
|
121
|
+
Polls an endpoint at a specified interval but automatically pauses or throttles the frequency when the browser tab is in the background, saving network and compute resources.
|
|
122
|
+
|
|
123
|
+
**Signature**
|
|
124
|
+
|
|
125
|
+
```ts
|
|
126
|
+
useAdaptivePolling(callback, options);
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
**Options**
|
|
130
|
+
|
|
131
|
+
| Parameter | Type | Default | Description |
|
|
132
|
+
| -------------------------- | --------- | ---------- | ------------------------------------------------------------------------------------- |
|
|
133
|
+
| `interval` | `number` | _required_ | Polling interval in milliseconds |
|
|
134
|
+
| `enabled` | `boolean` | `true` | Toggle polling on or off |
|
|
135
|
+
| `pauseOnBackground` | `boolean` | `true` | Fully pause when the tab is hidden |
|
|
136
|
+
| `backgroundSlowdownFactor` | `number` | `5` | Multiplier applied to interval when hidden (used when `pauseOnBackground` is `false`) |
|
|
137
|
+
|
|
138
|
+
**Usage**
|
|
139
|
+
|
|
140
|
+
```tsx
|
|
141
|
+
import { useAdaptivePolling } from "react-hook-kit";
|
|
142
|
+
import { useState } from "react";
|
|
143
|
+
|
|
144
|
+
function Notifications() {
|
|
145
|
+
const [data, setData] = useState(null);
|
|
146
|
+
|
|
147
|
+
useAdaptivePolling(
|
|
148
|
+
() => {
|
|
149
|
+
fetch("/api/notifications")
|
|
150
|
+
.then((res) => res.json())
|
|
151
|
+
.then(setData);
|
|
152
|
+
},
|
|
153
|
+
{
|
|
154
|
+
interval: 3000,
|
|
155
|
+
pauseOnBackground: false,
|
|
156
|
+
backgroundSlowdownFactor: 4,
|
|
157
|
+
}
|
|
158
|
+
);
|
|
159
|
+
|
|
160
|
+
return <pre>{JSON.stringify(data, null, 2)}</pre>;
|
|
161
|
+
}
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
When the tab is hidden, the polling interval increases from 3 seconds to 12 seconds. When the user returns, polling resumes at normal speed and fires immediately.
|
|
165
|
+
|
|
166
|
+
---
|
|
167
|
+
|
|
168
|
+
## License
|
|
169
|
+
|
|
170
|
+
MIT
|
|
171
|
+
|
|
172
|
+
---
|
|
173
|
+
|
|
174
|
+
## Author
|
|
175
|
+
|
|
176
|
+
Tanush
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
type IdleQueueTask = () => void | Promise<void>;
|
|
2
|
+
interface UseIdleQueueOptions {
|
|
3
|
+
timeout?: number;
|
|
4
|
+
fallbackInterval?: number;
|
|
5
|
+
}
|
|
6
|
+
interface UseIdleQueueReturn {
|
|
7
|
+
enqueue: (task: IdleQueueTask) => void;
|
|
8
|
+
clearQueue: () => void;
|
|
9
|
+
queueLength: number;
|
|
10
|
+
}
|
|
11
|
+
declare function useIdleQueue(options?: UseIdleQueueOptions): UseIdleQueueReturn;
|
|
12
|
+
|
|
13
|
+
declare function useBroadcastState<T>(channelName: string, initialValue: T): [T, (value: T | ((prevState: T) => T)) => void];
|
|
14
|
+
|
|
15
|
+
interface UseAdaptivePollingOptions {
|
|
16
|
+
interval: number;
|
|
17
|
+
enabled?: boolean;
|
|
18
|
+
pauseOnBackground?: boolean;
|
|
19
|
+
backgroundSlowdownFactor?: number;
|
|
20
|
+
}
|
|
21
|
+
declare function useAdaptivePolling(callback: () => void, options: UseAdaptivePollingOptions): void;
|
|
22
|
+
|
|
23
|
+
export { type IdleQueueTask, type UseAdaptivePollingOptions, type UseIdleQueueOptions, type UseIdleQueueReturn, useAdaptivePolling, useBroadcastState, useIdleQueue };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
type IdleQueueTask = () => void | Promise<void>;
|
|
2
|
+
interface UseIdleQueueOptions {
|
|
3
|
+
timeout?: number;
|
|
4
|
+
fallbackInterval?: number;
|
|
5
|
+
}
|
|
6
|
+
interface UseIdleQueueReturn {
|
|
7
|
+
enqueue: (task: IdleQueueTask) => void;
|
|
8
|
+
clearQueue: () => void;
|
|
9
|
+
queueLength: number;
|
|
10
|
+
}
|
|
11
|
+
declare function useIdleQueue(options?: UseIdleQueueOptions): UseIdleQueueReturn;
|
|
12
|
+
|
|
13
|
+
declare function useBroadcastState<T>(channelName: string, initialValue: T): [T, (value: T | ((prevState: T) => T)) => void];
|
|
14
|
+
|
|
15
|
+
interface UseAdaptivePollingOptions {
|
|
16
|
+
interval: number;
|
|
17
|
+
enabled?: boolean;
|
|
18
|
+
pauseOnBackground?: boolean;
|
|
19
|
+
backgroundSlowdownFactor?: number;
|
|
20
|
+
}
|
|
21
|
+
declare function useAdaptivePolling(callback: () => void, options: UseAdaptivePollingOptions): void;
|
|
22
|
+
|
|
23
|
+
export { type IdleQueueTask, type UseAdaptivePollingOptions, type UseIdleQueueOptions, type UseIdleQueueReturn, useAdaptivePolling, useBroadcastState, useIdleQueue };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
'use strict';var react=require('react');var w=()=>typeof window<"u"&&typeof window.requestIdleCallback=="function";function x(i={}){let{timeout:d,fallbackInterval:r=50}=i,s=react.useRef([]),u=react.useRef(null),o=react.useRef(false),a=react.useRef(false),[f,n]=react.useState(0),e=react.useCallback(l=>{if(w()){let m=d!=null?{timeout:d}:{};u.current=window.requestIdleCallback(l,m);}else u.current=window.setTimeout(l,r);},[d,r]),t=react.useCallback(()=>{u.current!=null&&(w()?window.cancelIdleCallback(u.current):window.clearTimeout(u.current),u.current=null);},[]),c=react.useCallback(()=>{if(a.current||o.current)return;let l=s.current.shift();if(!l){o.current=false;return}o.current=true,n(s.current.length),(async()=>{try{await l();}catch(k){console.error("[useIdleQueue] Task threw an error:",k);}finally{o.current=false,!a.current&&s.current.length>0&&e(c);}})();},[e]),p=react.useCallback(l=>{a.current||(s.current.push(l),n(s.current.length),o.current||e(c));},[e,c]),y=react.useCallback(()=>{s.current=[],n(0),t(),o.current=false;},[t]);return react.useEffect(()=>(a.current=false,()=>{a.current=true,t(),s.current=[];}),[t]),{enqueue:p,clearQueue:y,queueLength:f}}function C(i,d){let[r,s]=react.useState(d),u=react.useRef(r);react.useEffect(()=>{u.current=r;},[r]);let o=react.useRef(null),a=react.useRef(false);react.useEffect(()=>{if(typeof BroadcastChannel>"u")return;let n=new BroadcastChannel(i);return o.current=n,n.onmessage=e=>{try{let t=typeof e.data=="string"?JSON.parse(e.data):e.data,c=JSON.stringify(u.current),p=JSON.stringify(t);c!==p&&(a.current=!0,s(t));}catch(t){console.error("[useBroadcastState] Failed to parse incoming message:",t);}},n.onmessageerror=e=>{console.error("[useBroadcastState] Message deserialization error:",e);},()=>{n.close(),o.current=null;}},[i]);let f=react.useCallback(n=>{let e=typeof n=="function"?n(u.current):n;if(u.current=e,s(e),o.current)try{o.current.postMessage(JSON.stringify(e));}catch(t){console.error("[useBroadcastState] Failed to broadcast state:",t);}},[]);return react.useEffect(()=>{a.current&&(a.current=false);},[r]),[r,f]}function U(i,d){let{interval:r,enabled:s=true,pauseOnBackground:u=true,backgroundSlowdownFactor:o=5}=d,a=react.useRef(i);react.useEffect(()=>{a.current=i;},[i]);let f=react.useRef(0),n=react.useRef(null),e=react.useCallback(()=>{f.current=Date.now(),a.current();},[]),t=react.useCallback(()=>{n.current!=null&&(clearInterval(n.current),n.current=null);},[]),c=react.useCallback(p=>{t(),n.current=setInterval(e,p);},[t,e]);react.useEffect(()=>{if(!s){t();return}let l=typeof document<"u"&&document.visibilityState==="hidden"?u?0:r*o:r;l>0&&c(l);let m=()=>{document.visibilityState==="visible"?(Date.now()-f.current>=r&&e(),c(r)):u?t():c(r*o);};return typeof document<"u"&&document.addEventListener("visibilitychange",m),()=>{t(),typeof document<"u"&&document.removeEventListener("visibilitychange",m);}},[r,s,u,o,c,t,e]);}exports.useAdaptivePolling=U;exports.useBroadcastState=C;exports.useIdleQueue=x;//# sourceMappingURL=index.js.map
|
|
2
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/hooks/useIdleQueue.ts","../src/hooks/useBroadcastState.ts","../src/hooks/useAdaptivePolling.ts"],"names":["hasIdleCallback","useIdleQueue","options","timeout","fallbackInterval","queueRef","useRef","handleRef","processingRef","unmountedRef","queueLength","setQueueLength","useState","schedule","useCallback","fn","opts","cancelScheduled","processQueue","task","error","enqueue","clearQueue","useEffect","useBroadcastState","channelName","initialValue","state","setState","stateRef","channelRef","isExternalUpdateRef","channel","event","incoming","current","next","setBroadcastState","value","nextValue","useAdaptivePolling","callback","interval","enabled","pauseOnBackground","backgroundSlowdownFactor","callbackRef","lastFiredRef","intervalIdRef","fire","clearTimer","startTimer","ms","startingInterval","onVisibilityChange"],"mappings":"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","file":"index.js","sourcesContent":["import { useState, useRef, useCallback, useEffect } from 'react';\r\n\r\nexport type IdleQueueTask = () => void | Promise<void>;\r\n\r\nexport interface UseIdleQueueOptions {\r\n timeout?: number;\r\n fallbackInterval?: number;\r\n}\r\n\r\nexport interface UseIdleQueueReturn {\r\n enqueue: (task: IdleQueueTask) => void;\r\n clearQueue: () => void;\r\n queueLength: number;\r\n}\r\n\r\nconst hasIdleCallback = (): boolean =>\r\n typeof window !== 'undefined' &&\r\n typeof window.requestIdleCallback === 'function';\r\n\r\nexport function useIdleQueue(options: UseIdleQueueOptions = {}): UseIdleQueueReturn {\r\n const { timeout, fallbackInterval = 50 } = options;\r\n\r\n const queueRef = useRef<IdleQueueTask[]>([]);\r\n const handleRef = useRef<number | null>(null);\r\n const processingRef = useRef(false);\r\n const unmountedRef = useRef(false);\r\n\r\n const [queueLength, setQueueLength] = useState(0);\r\n\r\n const schedule = useCallback(\r\n (fn: () => void) => {\r\n if (hasIdleCallback()) {\r\n const opts: IdleRequestOptions = timeout != null ? { timeout } : {};\r\n handleRef.current = window.requestIdleCallback(fn, opts);\r\n } else {\r\n handleRef.current = window.setTimeout(fn, fallbackInterval) as unknown as number;\r\n }\r\n },\r\n [timeout, fallbackInterval],\r\n );\r\n\r\n const cancelScheduled = useCallback(() => {\r\n if (handleRef.current == null) return;\r\n\r\n if (hasIdleCallback()) {\r\n window.cancelIdleCallback(handleRef.current);\r\n } else {\r\n window.clearTimeout(handleRef.current);\r\n }\r\n handleRef.current = null;\r\n }, []);\r\n\r\n const processQueue = useCallback(() => {\r\n if (unmountedRef.current || processingRef.current) return;\r\n\r\n const task = queueRef.current.shift();\r\n if (!task) {\r\n processingRef.current = false;\r\n return;\r\n }\r\n\r\n processingRef.current = true;\r\n setQueueLength(queueRef.current.length);\r\n\r\n const execute = async () => {\r\n try {\r\n await task();\r\n } catch (error) {\r\n console.error('[useIdleQueue] Task threw an error:', error);\r\n } finally {\r\n processingRef.current = false;\r\n if (!unmountedRef.current && queueRef.current.length > 0) {\r\n schedule(processQueue);\r\n }\r\n }\r\n };\r\n\r\n void execute();\r\n }, [schedule]);\r\n\r\n const enqueue = useCallback(\r\n (task: IdleQueueTask) => {\r\n if (unmountedRef.current) return;\r\n\r\n queueRef.current.push(task);\r\n setQueueLength(queueRef.current.length);\r\n\r\n if (!processingRef.current) {\r\n schedule(processQueue);\r\n }\r\n },\r\n [schedule, processQueue],\r\n );\r\n\r\n const clearQueue = useCallback(() => {\r\n queueRef.current = [];\r\n setQueueLength(0);\r\n cancelScheduled();\r\n processingRef.current = false;\r\n }, [cancelScheduled]);\r\n\r\n useEffect(() => {\r\n unmountedRef.current = false;\r\n\r\n return () => {\r\n unmountedRef.current = true;\r\n cancelScheduled();\r\n queueRef.current = [];\r\n };\r\n }, [cancelScheduled]);\r\n\r\n return { enqueue, clearQueue, queueLength };\r\n}\r\n","import { useState, useRef, useCallback, useEffect } from 'react';\r\n\r\nexport function useBroadcastState<T>(\r\n channelName: string,\r\n initialValue: T,\r\n): [T, (value: T | ((prevState: T) => T)) => void] {\r\n const [state, setState] = useState<T>(initialValue);\r\n\r\n const stateRef = useRef<T>(state);\r\n useEffect(() => {\r\n stateRef.current = state;\r\n }, [state]);\r\n\r\n const channelRef = useRef<BroadcastChannel | null>(null);\r\n const isExternalUpdateRef = useRef(false);\r\n\r\n useEffect(() => {\r\n if (typeof BroadcastChannel === 'undefined') return;\r\n\r\n const channel = new BroadcastChannel(channelName);\r\n channelRef.current = channel;\r\n\r\n channel.onmessage = (event: MessageEvent) => {\r\n try {\r\n const incoming: T =\r\n typeof event.data === 'string'\r\n ? (JSON.parse(event.data) as T)\r\n : (event.data as T);\r\n\r\n const current = JSON.stringify(stateRef.current);\r\n const next = JSON.stringify(incoming);\r\n\r\n if (current !== next) {\r\n isExternalUpdateRef.current = true;\r\n setState(incoming);\r\n }\r\n } catch (error) {\r\n console.error(\r\n '[useBroadcastState] Failed to parse incoming message:',\r\n error,\r\n );\r\n }\r\n };\r\n\r\n channel.onmessageerror = (event: MessageEvent) => {\r\n console.error(\r\n '[useBroadcastState] Message deserialization error:',\r\n event,\r\n );\r\n };\r\n\r\n return () => {\r\n channel.close();\r\n channelRef.current = null;\r\n };\r\n }, [channelName]);\r\n\r\n const setBroadcastState = useCallback(\r\n (value: T | ((prevState: T) => T)) => {\r\n const nextValue: T =\r\n typeof value === 'function'\r\n ? (value as (prevState: T) => T)(stateRef.current)\r\n : value;\r\n\r\n stateRef.current = nextValue;\r\n setState(nextValue);\r\n\r\n if (channelRef.current) {\r\n try {\r\n channelRef.current.postMessage(JSON.stringify(nextValue));\r\n } catch (error) {\r\n console.error(\r\n '[useBroadcastState] Failed to broadcast state:',\r\n error,\r\n );\r\n }\r\n }\r\n },\r\n [],\r\n );\r\n\r\n useEffect(() => {\r\n if (isExternalUpdateRef.current) {\r\n isExternalUpdateRef.current = false;\r\n }\r\n }, [state]);\r\n\r\n return [state, setBroadcastState];\r\n}\r\n","import { useEffect, useRef, useCallback } from 'react';\r\n\r\nexport interface UseAdaptivePollingOptions {\r\n interval: number;\r\n enabled?: boolean;\r\n pauseOnBackground?: boolean;\r\n backgroundSlowdownFactor?: number;\r\n}\r\n\r\nexport function useAdaptivePolling(\r\n callback: () => void,\r\n options: UseAdaptivePollingOptions,\r\n): void {\r\n const {\r\n interval,\r\n enabled = true,\r\n pauseOnBackground = true,\r\n backgroundSlowdownFactor = 5,\r\n } = options;\r\n\r\n const callbackRef = useRef(callback);\r\n useEffect(() => {\r\n callbackRef.current = callback;\r\n }, [callback]);\r\n\r\n const lastFiredRef = useRef<number>(0);\r\n const intervalIdRef = useRef<ReturnType<typeof setInterval> | null>(null);\r\n\r\n const fire = useCallback(() => {\r\n lastFiredRef.current = Date.now();\r\n callbackRef.current();\r\n }, []);\r\n\r\n const clearTimer = useCallback(() => {\r\n if (intervalIdRef.current != null) {\r\n clearInterval(intervalIdRef.current);\r\n intervalIdRef.current = null;\r\n }\r\n }, []);\r\n\r\n const startTimer = useCallback(\r\n (ms: number) => {\r\n clearTimer();\r\n intervalIdRef.current = setInterval(fire, ms);\r\n },\r\n [clearTimer, fire],\r\n );\r\n\r\n useEffect(() => {\r\n if (!enabled) {\r\n clearTimer();\r\n return;\r\n }\r\n\r\n const isHidden = (): boolean =>\r\n typeof document !== 'undefined' && document.visibilityState === 'hidden';\r\n\r\n const effectiveInterval = (): number => {\r\n if (!isHidden()) return interval;\r\n if (pauseOnBackground) return 0;\r\n return interval * backgroundSlowdownFactor;\r\n };\r\n\r\n const startingInterval = effectiveInterval();\r\n if (startingInterval > 0) {\r\n startTimer(startingInterval);\r\n }\r\n\r\n const onVisibilityChange = () => {\r\n if (document.visibilityState === 'visible') {\r\n const elapsed = Date.now() - lastFiredRef.current;\r\n if (elapsed >= interval) {\r\n fire();\r\n }\r\n startTimer(interval);\r\n } else {\r\n if (pauseOnBackground) {\r\n clearTimer();\r\n } else {\r\n startTimer(interval * backgroundSlowdownFactor);\r\n }\r\n }\r\n };\r\n\r\n if (typeof document !== 'undefined') {\r\n document.addEventListener('visibilitychange', onVisibilityChange);\r\n }\r\n\r\n return () => {\r\n clearTimer();\r\n if (typeof document !== 'undefined') {\r\n document.removeEventListener('visibilitychange', onVisibilityChange);\r\n }\r\n };\r\n }, [interval, enabled, pauseOnBackground, backgroundSlowdownFactor, startTimer, clearTimer, fire]);\r\n}\r\n"]}
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import {useRef,useState,useCallback,useEffect}from'react';var w=()=>typeof window<"u"&&typeof window.requestIdleCallback=="function";function x(i={}){let{timeout:d,fallbackInterval:r=50}=i,s=useRef([]),u=useRef(null),o=useRef(false),a=useRef(false),[f,n]=useState(0),e=useCallback(l=>{if(w()){let m=d!=null?{timeout:d}:{};u.current=window.requestIdleCallback(l,m);}else u.current=window.setTimeout(l,r);},[d,r]),t=useCallback(()=>{u.current!=null&&(w()?window.cancelIdleCallback(u.current):window.clearTimeout(u.current),u.current=null);},[]),c=useCallback(()=>{if(a.current||o.current)return;let l=s.current.shift();if(!l){o.current=false;return}o.current=true,n(s.current.length),(async()=>{try{await l();}catch(k){console.error("[useIdleQueue] Task threw an error:",k);}finally{o.current=false,!a.current&&s.current.length>0&&e(c);}})();},[e]),p=useCallback(l=>{a.current||(s.current.push(l),n(s.current.length),o.current||e(c));},[e,c]),y=useCallback(()=>{s.current=[],n(0),t(),o.current=false;},[t]);return useEffect(()=>(a.current=false,()=>{a.current=true,t(),s.current=[];}),[t]),{enqueue:p,clearQueue:y,queueLength:f}}function C(i,d){let[r,s]=useState(d),u=useRef(r);useEffect(()=>{u.current=r;},[r]);let o=useRef(null),a=useRef(false);useEffect(()=>{if(typeof BroadcastChannel>"u")return;let n=new BroadcastChannel(i);return o.current=n,n.onmessage=e=>{try{let t=typeof e.data=="string"?JSON.parse(e.data):e.data,c=JSON.stringify(u.current),p=JSON.stringify(t);c!==p&&(a.current=!0,s(t));}catch(t){console.error("[useBroadcastState] Failed to parse incoming message:",t);}},n.onmessageerror=e=>{console.error("[useBroadcastState] Message deserialization error:",e);},()=>{n.close(),o.current=null;}},[i]);let f=useCallback(n=>{let e=typeof n=="function"?n(u.current):n;if(u.current=e,s(e),o.current)try{o.current.postMessage(JSON.stringify(e));}catch(t){console.error("[useBroadcastState] Failed to broadcast state:",t);}},[]);return useEffect(()=>{a.current&&(a.current=false);},[r]),[r,f]}function U(i,d){let{interval:r,enabled:s=true,pauseOnBackground:u=true,backgroundSlowdownFactor:o=5}=d,a=useRef(i);useEffect(()=>{a.current=i;},[i]);let f=useRef(0),n=useRef(null),e=useCallback(()=>{f.current=Date.now(),a.current();},[]),t=useCallback(()=>{n.current!=null&&(clearInterval(n.current),n.current=null);},[]),c=useCallback(p=>{t(),n.current=setInterval(e,p);},[t,e]);useEffect(()=>{if(!s){t();return}let l=typeof document<"u"&&document.visibilityState==="hidden"?u?0:r*o:r;l>0&&c(l);let m=()=>{document.visibilityState==="visible"?(Date.now()-f.current>=r&&e(),c(r)):u?t():c(r*o);};return typeof document<"u"&&document.addEventListener("visibilitychange",m),()=>{t(),typeof document<"u"&&document.removeEventListener("visibilitychange",m);}},[r,s,u,o,c,t,e]);}export{U as useAdaptivePolling,C as useBroadcastState,x as useIdleQueue};//# sourceMappingURL=index.mjs.map
|
|
2
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/hooks/useIdleQueue.ts","../src/hooks/useBroadcastState.ts","../src/hooks/useAdaptivePolling.ts"],"names":["hasIdleCallback","useIdleQueue","options","timeout","fallbackInterval","queueRef","useRef","handleRef","processingRef","unmountedRef","queueLength","setQueueLength","useState","schedule","useCallback","fn","opts","cancelScheduled","processQueue","task","error","enqueue","clearQueue","useEffect","useBroadcastState","channelName","initialValue","state","setState","stateRef","channelRef","isExternalUpdateRef","channel","event","incoming","current","next","setBroadcastState","value","nextValue","useAdaptivePolling","callback","interval","enabled","pauseOnBackground","backgroundSlowdownFactor","callbackRef","lastFiredRef","intervalIdRef","fire","clearTimer","startTimer","ms","startingInterval","onVisibilityChange"],"mappings":"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","file":"index.mjs","sourcesContent":["import { useState, useRef, useCallback, useEffect } from 'react';\r\n\r\nexport type IdleQueueTask = () => void | Promise<void>;\r\n\r\nexport interface UseIdleQueueOptions {\r\n timeout?: number;\r\n fallbackInterval?: number;\r\n}\r\n\r\nexport interface UseIdleQueueReturn {\r\n enqueue: (task: IdleQueueTask) => void;\r\n clearQueue: () => void;\r\n queueLength: number;\r\n}\r\n\r\nconst hasIdleCallback = (): boolean =>\r\n typeof window !== 'undefined' &&\r\n typeof window.requestIdleCallback === 'function';\r\n\r\nexport function useIdleQueue(options: UseIdleQueueOptions = {}): UseIdleQueueReturn {\r\n const { timeout, fallbackInterval = 50 } = options;\r\n\r\n const queueRef = useRef<IdleQueueTask[]>([]);\r\n const handleRef = useRef<number | null>(null);\r\n const processingRef = useRef(false);\r\n const unmountedRef = useRef(false);\r\n\r\n const [queueLength, setQueueLength] = useState(0);\r\n\r\n const schedule = useCallback(\r\n (fn: () => void) => {\r\n if (hasIdleCallback()) {\r\n const opts: IdleRequestOptions = timeout != null ? { timeout } : {};\r\n handleRef.current = window.requestIdleCallback(fn, opts);\r\n } else {\r\n handleRef.current = window.setTimeout(fn, fallbackInterval) as unknown as number;\r\n }\r\n },\r\n [timeout, fallbackInterval],\r\n );\r\n\r\n const cancelScheduled = useCallback(() => {\r\n if (handleRef.current == null) return;\r\n\r\n if (hasIdleCallback()) {\r\n window.cancelIdleCallback(handleRef.current);\r\n } else {\r\n window.clearTimeout(handleRef.current);\r\n }\r\n handleRef.current = null;\r\n }, []);\r\n\r\n const processQueue = useCallback(() => {\r\n if (unmountedRef.current || processingRef.current) return;\r\n\r\n const task = queueRef.current.shift();\r\n if (!task) {\r\n processingRef.current = false;\r\n return;\r\n }\r\n\r\n processingRef.current = true;\r\n setQueueLength(queueRef.current.length);\r\n\r\n const execute = async () => {\r\n try {\r\n await task();\r\n } catch (error) {\r\n console.error('[useIdleQueue] Task threw an error:', error);\r\n } finally {\r\n processingRef.current = false;\r\n if (!unmountedRef.current && queueRef.current.length > 0) {\r\n schedule(processQueue);\r\n }\r\n }\r\n };\r\n\r\n void execute();\r\n }, [schedule]);\r\n\r\n const enqueue = useCallback(\r\n (task: IdleQueueTask) => {\r\n if (unmountedRef.current) return;\r\n\r\n queueRef.current.push(task);\r\n setQueueLength(queueRef.current.length);\r\n\r\n if (!processingRef.current) {\r\n schedule(processQueue);\r\n }\r\n },\r\n [schedule, processQueue],\r\n );\r\n\r\n const clearQueue = useCallback(() => {\r\n queueRef.current = [];\r\n setQueueLength(0);\r\n cancelScheduled();\r\n processingRef.current = false;\r\n }, [cancelScheduled]);\r\n\r\n useEffect(() => {\r\n unmountedRef.current = false;\r\n\r\n return () => {\r\n unmountedRef.current = true;\r\n cancelScheduled();\r\n queueRef.current = [];\r\n };\r\n }, [cancelScheduled]);\r\n\r\n return { enqueue, clearQueue, queueLength };\r\n}\r\n","import { useState, useRef, useCallback, useEffect } from 'react';\r\n\r\nexport function useBroadcastState<T>(\r\n channelName: string,\r\n initialValue: T,\r\n): [T, (value: T | ((prevState: T) => T)) => void] {\r\n const [state, setState] = useState<T>(initialValue);\r\n\r\n const stateRef = useRef<T>(state);\r\n useEffect(() => {\r\n stateRef.current = state;\r\n }, [state]);\r\n\r\n const channelRef = useRef<BroadcastChannel | null>(null);\r\n const isExternalUpdateRef = useRef(false);\r\n\r\n useEffect(() => {\r\n if (typeof BroadcastChannel === 'undefined') return;\r\n\r\n const channel = new BroadcastChannel(channelName);\r\n channelRef.current = channel;\r\n\r\n channel.onmessage = (event: MessageEvent) => {\r\n try {\r\n const incoming: T =\r\n typeof event.data === 'string'\r\n ? (JSON.parse(event.data) as T)\r\n : (event.data as T);\r\n\r\n const current = JSON.stringify(stateRef.current);\r\n const next = JSON.stringify(incoming);\r\n\r\n if (current !== next) {\r\n isExternalUpdateRef.current = true;\r\n setState(incoming);\r\n }\r\n } catch (error) {\r\n console.error(\r\n '[useBroadcastState] Failed to parse incoming message:',\r\n error,\r\n );\r\n }\r\n };\r\n\r\n channel.onmessageerror = (event: MessageEvent) => {\r\n console.error(\r\n '[useBroadcastState] Message deserialization error:',\r\n event,\r\n );\r\n };\r\n\r\n return () => {\r\n channel.close();\r\n channelRef.current = null;\r\n };\r\n }, [channelName]);\r\n\r\n const setBroadcastState = useCallback(\r\n (value: T | ((prevState: T) => T)) => {\r\n const nextValue: T =\r\n typeof value === 'function'\r\n ? (value as (prevState: T) => T)(stateRef.current)\r\n : value;\r\n\r\n stateRef.current = nextValue;\r\n setState(nextValue);\r\n\r\n if (channelRef.current) {\r\n try {\r\n channelRef.current.postMessage(JSON.stringify(nextValue));\r\n } catch (error) {\r\n console.error(\r\n '[useBroadcastState] Failed to broadcast state:',\r\n error,\r\n );\r\n }\r\n }\r\n },\r\n [],\r\n );\r\n\r\n useEffect(() => {\r\n if (isExternalUpdateRef.current) {\r\n isExternalUpdateRef.current = false;\r\n }\r\n }, [state]);\r\n\r\n return [state, setBroadcastState];\r\n}\r\n","import { useEffect, useRef, useCallback } from 'react';\r\n\r\nexport interface UseAdaptivePollingOptions {\r\n interval: number;\r\n enabled?: boolean;\r\n pauseOnBackground?: boolean;\r\n backgroundSlowdownFactor?: number;\r\n}\r\n\r\nexport function useAdaptivePolling(\r\n callback: () => void,\r\n options: UseAdaptivePollingOptions,\r\n): void {\r\n const {\r\n interval,\r\n enabled = true,\r\n pauseOnBackground = true,\r\n backgroundSlowdownFactor = 5,\r\n } = options;\r\n\r\n const callbackRef = useRef(callback);\r\n useEffect(() => {\r\n callbackRef.current = callback;\r\n }, [callback]);\r\n\r\n const lastFiredRef = useRef<number>(0);\r\n const intervalIdRef = useRef<ReturnType<typeof setInterval> | null>(null);\r\n\r\n const fire = useCallback(() => {\r\n lastFiredRef.current = Date.now();\r\n callbackRef.current();\r\n }, []);\r\n\r\n const clearTimer = useCallback(() => {\r\n if (intervalIdRef.current != null) {\r\n clearInterval(intervalIdRef.current);\r\n intervalIdRef.current = null;\r\n }\r\n }, []);\r\n\r\n const startTimer = useCallback(\r\n (ms: number) => {\r\n clearTimer();\r\n intervalIdRef.current = setInterval(fire, ms);\r\n },\r\n [clearTimer, fire],\r\n );\r\n\r\n useEffect(() => {\r\n if (!enabled) {\r\n clearTimer();\r\n return;\r\n }\r\n\r\n const isHidden = (): boolean =>\r\n typeof document !== 'undefined' && document.visibilityState === 'hidden';\r\n\r\n const effectiveInterval = (): number => {\r\n if (!isHidden()) return interval;\r\n if (pauseOnBackground) return 0;\r\n return interval * backgroundSlowdownFactor;\r\n };\r\n\r\n const startingInterval = effectiveInterval();\r\n if (startingInterval > 0) {\r\n startTimer(startingInterval);\r\n }\r\n\r\n const onVisibilityChange = () => {\r\n if (document.visibilityState === 'visible') {\r\n const elapsed = Date.now() - lastFiredRef.current;\r\n if (elapsed >= interval) {\r\n fire();\r\n }\r\n startTimer(interval);\r\n } else {\r\n if (pauseOnBackground) {\r\n clearTimer();\r\n } else {\r\n startTimer(interval * backgroundSlowdownFactor);\r\n }\r\n }\r\n };\r\n\r\n if (typeof document !== 'undefined') {\r\n document.addEventListener('visibilitychange', onVisibilityChange);\r\n }\r\n\r\n return () => {\r\n clearTimer();\r\n if (typeof document !== 'undefined') {\r\n document.removeEventListener('visibilitychange', onVisibilityChange);\r\n }\r\n };\r\n }, [interval, enabled, pauseOnBackground, backgroundSlowdownFactor, startTimer, clearTimer, fire]);\r\n}\r\n"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "use-web-kit",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "A highly optimized, zero-dependency, lightweight npm library for React custom hooks focusing on performance and Browser APIs.",
|
|
5
|
+
"main": "dist/index.cjs",
|
|
6
|
+
"module": "dist/index.js",
|
|
7
|
+
"types": "dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"import": {
|
|
11
|
+
"types": "./dist/index.d.ts",
|
|
12
|
+
"default": "./dist/index.js"
|
|
13
|
+
},
|
|
14
|
+
"require": {
|
|
15
|
+
"types": "./dist/index.d.cts",
|
|
16
|
+
"default": "./dist/index.cjs"
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
},
|
|
20
|
+
"files": [
|
|
21
|
+
"dist"
|
|
22
|
+
],
|
|
23
|
+
"scripts": {
|
|
24
|
+
"build": "tsup",
|
|
25
|
+
"name": "use-web-kit",
|
|
26
|
+
"test:watch": "jest --watch",
|
|
27
|
+
"lint": "tsc --noEmit",
|
|
28
|
+
"prepublishOnly": "npm run build"
|
|
29
|
+
},
|
|
30
|
+
"keywords": [
|
|
31
|
+
"react",
|
|
32
|
+
"hooks",
|
|
33
|
+
"custom-hooks",
|
|
34
|
+
"useIdleQueue",
|
|
35
|
+
"useBroadcastState",
|
|
36
|
+
"useAdaptivePolling",
|
|
37
|
+
"requestIdleCallback",
|
|
38
|
+
"BroadcastChannel",
|
|
39
|
+
"Page Visibility API",
|
|
40
|
+
"polling",
|
|
41
|
+
"typescript"
|
|
42
|
+
],
|
|
43
|
+
"author": "Tanush Bhootra <https://github.com/tanushbhootra576>",
|
|
44
|
+
"license": "MIT",
|
|
45
|
+
"repository": {
|
|
46
|
+
"type": "git",
|
|
47
|
+
"url": "git+https://github.com/tanushbhootra576/use-web-kit.git"
|
|
48
|
+
},
|
|
49
|
+
"bugs": {
|
|
50
|
+
"url": "https://github.com/tanushbhootra576/use-web-kit/issues"
|
|
51
|
+
},
|
|
52
|
+
"homepage": "https://github.com/tanushbhootra576/use-web-kit#readme",
|
|
53
|
+
"peerDependencies": {
|
|
54
|
+
"react": ">=16.8.0"
|
|
55
|
+
},
|
|
56
|
+
"devDependencies": {
|
|
57
|
+
"@testing-library/react": "^16.1.0",
|
|
58
|
+
"@testing-library/jest-dom": "^6.6.3",
|
|
59
|
+
"@types/jest": "^29.5.14",
|
|
60
|
+
"@types/react": "^19.0.0",
|
|
61
|
+
"@types/react-dom": "^19.0.0",
|
|
62
|
+
"jest": "^29.7.0",
|
|
63
|
+
"jest-environment-jsdom": "^29.7.0",
|
|
64
|
+
"react": "^19.0.0",
|
|
65
|
+
"react-dom": "^19.0.0",
|
|
66
|
+
"ts-jest": "^29.2.5",
|
|
67
|
+
"tsup": "^8.3.5",
|
|
68
|
+
"typescript": "^5.7.0"
|
|
69
|
+
}
|
|
70
|
+
}
|