react-visibility-hooks 1.0.8 → 1.0.9
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 +33 -10
- package/dist/index.cjs +1 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -98,7 +98,7 @@ function VideoPlayer() {
|
|
|
98
98
|
|
|
99
99
|
### `useSmartPolling`
|
|
100
100
|
|
|
101
|
-
|
|
101
|
+
Visibility-aware polling built with plain React — no external dependencies. Automatically pauses when the tab is hidden, resumes when visible, and skips re-renders when data hasn't changed.
|
|
102
102
|
|
|
103
103
|
**Use cases:**
|
|
104
104
|
- Real-time dashboards
|
|
@@ -109,22 +109,45 @@ Smart polling that only fetches data when the page is visible. Zero additional d
|
|
|
109
109
|
import { useSmartPolling } from 'react-visibility-hooks';
|
|
110
110
|
|
|
111
111
|
function Dashboard() {
|
|
112
|
-
const { data, isLoading } = useSmartPolling(
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
112
|
+
const { data, isLoading, isError, error, refetch } = useSmartPolling(
|
|
113
|
+
async () => {
|
|
114
|
+
const response = await fetch('/api/stats');
|
|
115
|
+
return response.json();
|
|
116
|
+
},
|
|
117
|
+
{ interval: 3000 }
|
|
118
|
+
);
|
|
116
119
|
|
|
117
120
|
if (isLoading) return <div>Loading...</div>;
|
|
121
|
+
if (isError) return <div>Error: {error?.message}</div>;
|
|
118
122
|
|
|
119
|
-
return
|
|
123
|
+
return (
|
|
124
|
+
<div>
|
|
125
|
+
<pre>{JSON.stringify(data, null, 2)}</pre>
|
|
126
|
+
<button onClick={refetch}>Refresh now</button>
|
|
127
|
+
</div>
|
|
128
|
+
);
|
|
120
129
|
}
|
|
121
130
|
```
|
|
122
131
|
|
|
123
132
|
**Options:**
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
133
|
+
| Option | Type | Default | Description |
|
|
134
|
+
|--------|------|---------|-------------|
|
|
135
|
+
| `interval` | `number` | `5000` | Polling interval in milliseconds |
|
|
136
|
+
| `enabled` | `boolean` | `true` | Enable/disable polling |
|
|
137
|
+
|
|
138
|
+
**Returns:**
|
|
139
|
+
| Property | Type | Description |
|
|
140
|
+
|----------|------|-------------|
|
|
141
|
+
| `data` | `T \| undefined` | The latest fetched data |
|
|
142
|
+
| `isLoading` | `boolean` | `true` until the first fetch completes |
|
|
143
|
+
| `isError` | `boolean` | `true` if the last fetch threw |
|
|
144
|
+
| `error` | `Error \| undefined` | The error object, if any |
|
|
145
|
+
| `refetch` | `() => void` | Manually trigger a fetch |
|
|
146
|
+
|
|
147
|
+
**Optimizations:**
|
|
148
|
+
- Skips re-renders when polled data is identical to the previous result
|
|
149
|
+
- Prevents concurrent fetches from overlapping
|
|
150
|
+
- Polling pauses automatically when the tab is hidden and resumes when visible
|
|
128
151
|
|
|
129
152
|
## SSR Support
|
|
130
153
|
|
package/dist/index.cjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use strict";var
|
|
1
|
+
"use strict";var v=Object.defineProperty;var x=Object.getOwnPropertyDescriptor;var h=Object.getOwnPropertyNames;var V=Object.prototype.hasOwnProperty;var I=(t,e)=>{for(var r in e)v(t,r,{get:e[r],enumerable:!0})},P=(t,e,r,o)=>{if(e&&typeof e=="object"||typeof e=="function")for(let i of h(e))!V.call(t,i)&&i!==r&&v(t,i,{get:()=>e[i],enumerable:!(o=x(e,i))||o.enumerable});return t};var D=t=>P(v({},"__esModule",{value:!0}),t);var J={};I(J,{useAutoPauseVideo:()=>k,useDocVisible:()=>c,useIdleVisibility:()=>O,useSmartPolling:()=>j});module.exports=D(J);var f=require("react");function c(){let[t,e]=(0,f.useState)(typeof document=="undefined"||document.visibilityState==="visible");return(0,f.useEffect)(()=>{if(typeof document=="undefined")return;let r=()=>e(document.visibilityState==="visible");return document.addEventListener("visibilitychange",r),()=>document.removeEventListener("visibilitychange",r)},[]),t}var l=require("react");function O(t=6e4){let e=c(),[r,o]=(0,l.useState)(!1);return(0,l.useEffect)(()=>{let i,u=()=>{o(!1),clearTimeout(i),i=setTimeout(()=>o(!0),t)};return window.addEventListener("mousemove",u),window.addEventListener("keydown",u),u(),()=>{window.removeEventListener("mousemove",u),window.removeEventListener("keydown",u),clearTimeout(i)}},[t]),{visible:e,idle:r}}var S=require("react");function k(t){let e=c();(0,S.useEffect)(()=>{let r=t.current;r&&(e?r.play().catch(()=>{}):r.pause())},[e,t])}var n=require("react");function j(t,e){let{interval:r=5e3,enabled:o=!0}=e!=null?e:{},i=c(),[u,T]=(0,n.useState)(void 0),[L,R]=(0,n.useState)(!0),[b,y]=(0,n.useState)(void 0),E=(0,n.useRef)(t),d=(0,n.useRef)(void 0),m=(0,n.useRef)(!1),p=(0,n.useRef)(void 0);E.current=t;let a=(0,n.useCallback)(async()=>{if(!m.current){m.current=!0;try{let s=await E.current(),g=JSON.stringify(s);g!==p.current&&(p.current=g,T(s)),y(w=>w!==void 0?void 0:w)}catch(s){y(s instanceof Error?s:new Error(String(s)))}finally{m.current=!1,R(s=>s&&!1)}}},[]);return(0,n.useEffect)(()=>{o&&a()},[o,a]),(0,n.useEffect)(()=>{if(!o||!i){clearInterval(d.current);return}return d.current=setInterval(a,r),()=>clearInterval(d.current)},[i,o,r,a]),{data:u,isLoading:L,isError:!!b,error:b,refetch:a}}0&&(module.exports={useAutoPauseVideo,useDocVisible,useIdleVisibility,useSmartPolling});
|
|
2
2
|
//# sourceMappingURL=index.cjs.map
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/useDocVisible.ts","../src/useIdleVisibility.ts","../src/useAutoPauseVideo.ts","../src/useSmartPolling.ts"],"sourcesContent":["export * from \"./useDocVisible\";\nexport * from \"./useIdleVisibility\";\nexport * from \"./useAutoPauseVideo\";\nexport * from \"./useSmartPolling\";\n","import { useEffect, useState } from \"react\";\n\nexport function useDocVisible(): boolean {\n const [visible, setVisible] = useState(\n typeof document === \"undefined\" || document.visibilityState === \"visible\"\n );\n\n useEffect(() => {\n if (typeof document === \"undefined\") return;\n const handler = () => setVisible(document.visibilityState === \"visible\");\n document.addEventListener(\"visibilitychange\", handler);\n return () => document.removeEventListener(\"visibilitychange\", handler);\n }, []);\n\n return visible;\n}\n","import { useEffect, useState } from \"react\";\nimport { useDocVisible } from \"./useDocVisible\";\n\nexport function useIdleVisibility(timeout = 60000) {\n const visible = useDocVisible();\n const [idle, setIdle] = useState(false);\n\n useEffect(() => {\n let timer: any;\n\n const reset = () => {\n setIdle(false);\n clearTimeout(timer);\n timer = setTimeout(() => setIdle(true), timeout);\n };\n\n window.addEventListener(\"mousemove\", reset);\n window.addEventListener(\"keydown\", reset);\n reset();\n\n return () => {\n window.removeEventListener(\"mousemove\", reset);\n window.removeEventListener(\"keydown\", reset);\n clearTimeout(timer);\n };\n }, [timeout]);\n\n return { visible, idle };\n}\n","import { useEffect } from \"react\";\nimport { useDocVisible } from \"./useDocVisible\";\n\nexport function useAutoPauseVideo(ref: React.RefObject<HTMLVideoElement>) {\n const visible = useDocVisible();\n\n useEffect(() => {\n const video = ref.current;\n if (!video) return;\n\n if (!visible) video.pause();\n else video.play().catch(() => {});\n }, [visible, ref]);\n}\n","import { useState, useEffect, useRef, useCallback } from \"react\";\nimport { useDocVisible } from \"./useDocVisible\";\n\ninterface SmartPollingOptions {\n interval?: number;\n enabled?: boolean;\n}\n\ninterface SmartPollingResult<T> {\n data: T | undefined;\n isLoading: boolean;\n isError: boolean;\n error: Error | undefined;\n refetch: () => void;\n}\n\nexport function useSmartPolling<T = any>(\n fetchFn: () => Promise<T>,\n options?: SmartPollingOptions,\n): SmartPollingResult<T> {\n const { interval = 5000, enabled = true } = options ?? {};\n const visible = useDocVisible();\n const [data, setData] = useState<T | undefined>(undefined);\n const [isLoading, setIsLoading] = useState(true);\n const [error, setError] = useState<Error | undefined>(undefined);\n const fetchRef = useRef(fetchFn);\n const timerRef = useRef<ReturnType<typeof setInterval> | undefined>(\n undefined,\n );\n\n fetchRef.current = fetchFn;\n\n const execute = useCallback(async () => {\n try {\n const result = await fetchRef.current();\n setData(result);\n setError(undefined);\n } catch (err) {\n setError(err instanceof Error ? err : new Error(String(err)));\n } finally {\n setIsLoading(false);\n }\n }, []);\n\n // Initial fetch\n useEffect(() => {\n if (!enabled) return;\n
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/useDocVisible.ts","../src/useIdleVisibility.ts","../src/useAutoPauseVideo.ts","../src/useSmartPolling.ts"],"sourcesContent":["export * from \"./useDocVisible\";\nexport * from \"./useIdleVisibility\";\nexport * from \"./useAutoPauseVideo\";\nexport * from \"./useSmartPolling\";\n","import { useEffect, useState } from \"react\";\n\nexport function useDocVisible(): boolean {\n const [visible, setVisible] = useState(\n typeof document === \"undefined\" || document.visibilityState === \"visible\"\n );\n\n useEffect(() => {\n if (typeof document === \"undefined\") return;\n const handler = () => setVisible(document.visibilityState === \"visible\");\n document.addEventListener(\"visibilitychange\", handler);\n return () => document.removeEventListener(\"visibilitychange\", handler);\n }, []);\n\n return visible;\n}\n","import { useEffect, useState } from \"react\";\nimport { useDocVisible } from \"./useDocVisible\";\n\nexport function useIdleVisibility(timeout = 60000) {\n const visible = useDocVisible();\n const [idle, setIdle] = useState(false);\n\n useEffect(() => {\n let timer: any;\n\n const reset = () => {\n setIdle(false);\n clearTimeout(timer);\n timer = setTimeout(() => setIdle(true), timeout);\n };\n\n window.addEventListener(\"mousemove\", reset);\n window.addEventListener(\"keydown\", reset);\n reset();\n\n return () => {\n window.removeEventListener(\"mousemove\", reset);\n window.removeEventListener(\"keydown\", reset);\n clearTimeout(timer);\n };\n }, [timeout]);\n\n return { visible, idle };\n}\n","import { useEffect } from \"react\";\nimport { useDocVisible } from \"./useDocVisible\";\n\nexport function useAutoPauseVideo(ref: React.RefObject<HTMLVideoElement>) {\n const visible = useDocVisible();\n\n useEffect(() => {\n const video = ref.current;\n if (!video) return;\n\n if (!visible) video.pause();\n else video.play().catch(() => {});\n }, [visible, ref]);\n}\n","import { useState, useEffect, useRef, useCallback } from \"react\";\nimport { useDocVisible } from \"./useDocVisible\";\n\ninterface SmartPollingOptions {\n interval?: number;\n enabled?: boolean;\n}\n\ninterface SmartPollingResult<T> {\n data: T | undefined;\n isLoading: boolean;\n isError: boolean;\n error: Error | undefined;\n refetch: () => void;\n}\n\nexport function useSmartPolling<T = any>(\n fetchFn: () => Promise<T>,\n options?: SmartPollingOptions,\n): SmartPollingResult<T> {\n const { interval = 5000, enabled = true } = options ?? {};\n const visible = useDocVisible();\n const [data, setData] = useState<T | undefined>(undefined);\n const [isLoading, setIsLoading] = useState(true);\n const [error, setError] = useState<Error | undefined>(undefined);\n const fetchRef = useRef(fetchFn);\n const timerRef = useRef<ReturnType<typeof setInterval> | undefined>(\n undefined,\n );\n const isFetchingRef = useRef(false);\n const lastJsonRef = useRef<string | undefined>(undefined);\n\n fetchRef.current = fetchFn;\n\n const execute = useCallback(async () => {\n // Prevent concurrent fetches from overlapping intervals\n if (isFetchingRef.current) return;\n isFetchingRef.current = true;\n\n try {\n const result = await fetchRef.current();\n\n // Only update state if data actually changed (avoids unnecessary re-renders)\n const json = JSON.stringify(result);\n if (json !== lastJsonRef.current) {\n lastJsonRef.current = json;\n setData(result);\n }\n setError((prev) => (prev !== undefined ? undefined : prev));\n } catch (err) {\n setError(err instanceof Error ? err : new Error(String(err)));\n } finally {\n isFetchingRef.current = false;\n setIsLoading((prev) => (prev ? false : prev));\n }\n }, []);\n\n // Initial fetch\n useEffect(() => {\n if (!enabled) return;\n execute();\n }, [enabled, execute]);\n\n // Polling — only when visible and enabled\n useEffect(() => {\n if (!enabled || !visible) {\n clearInterval(timerRef.current);\n return;\n }\n\n timerRef.current = setInterval(execute, interval);\n return () => clearInterval(timerRef.current);\n }, [visible, enabled, interval, execute]);\n\n return { data, isLoading, isError: !!error, error, refetch: execute };\n}\n"],"mappings":"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","names":["index_exports","__export","useAutoPauseVideo","useDocVisible","useIdleVisibility","useSmartPolling","__toCommonJS","import_react","useDocVisible","visible","setVisible","handler","import_react","useIdleVisibility","timeout","visible","useDocVisible","idle","setIdle","timer","reset","import_react","useAutoPauseVideo","ref","visible","useDocVisible","video","import_react","useSmartPolling","fetchFn","options","interval","enabled","visible","useDocVisible","data","setData","isLoading","setIsLoading","error","setError","fetchRef","timerRef","isFetchingRef","lastJsonRef","execute","result","json","prev","err"]}
|
package/dist/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{useEffect as
|
|
1
|
+
import{useEffect as L,useState as R}from"react";function u(){let[t,r]=R(typeof document=="undefined"||document.visibilityState==="visible");return L(()=>{if(typeof document=="undefined")return;let e=()=>r(document.visibilityState==="visible");return document.addEventListener("visibilitychange",e),()=>document.removeEventListener("visibilitychange",e)},[]),t}import{useEffect as x,useState as h}from"react";function j(t=6e4){let r=u(),[e,i]=h(!1);return x(()=>{let s,o=()=>{i(!1),clearTimeout(s),s=setTimeout(()=>i(!0),t)};return window.addEventListener("mousemove",o),window.addEventListener("keydown",o),o(),()=>{window.removeEventListener("mousemove",o),window.removeEventListener("keydown",o),clearTimeout(s)}},[t]),{visible:r,idle:e}}import{useEffect as V}from"react";function H(t){let r=u();V(()=>{let e=t.current;e&&(r?e.play().catch(()=>{}):e.pause())},[r,t])}import{useState as d,useEffect as g,useRef as a,useCallback as I}from"react";function z(t,r){let{interval:e=5e3,enabled:i=!0}=r!=null?r:{},s=u(),[o,w]=d(void 0),[S,T]=d(!0),[m,v]=d(void 0),b=a(t),f=a(void 0),l=a(!1),y=a(void 0);b.current=t;let c=I(async()=>{if(!l.current){l.current=!0;try{let n=await b.current(),E=JSON.stringify(n);E!==y.current&&(y.current=E,w(n)),v(p=>p!==void 0?void 0:p)}catch(n){v(n instanceof Error?n:new Error(String(n)))}finally{l.current=!1,T(n=>n&&!1)}}},[]);return g(()=>{i&&c()},[i,c]),g(()=>{if(!i||!s){clearInterval(f.current);return}return f.current=setInterval(c,e),()=>clearInterval(f.current)},[s,i,e,c]),{data:o,isLoading:S,isError:!!m,error:m,refetch:c}}export{H as useAutoPauseVideo,u as useDocVisible,j as useIdleVisibility,z as useSmartPolling};
|
|
2
2
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/useDocVisible.ts","../src/useIdleVisibility.ts","../src/useAutoPauseVideo.ts","../src/useSmartPolling.ts"],"sourcesContent":["import { useEffect, useState } from \"react\";\n\nexport function useDocVisible(): boolean {\n const [visible, setVisible] = useState(\n typeof document === \"undefined\" || document.visibilityState === \"visible\"\n );\n\n useEffect(() => {\n if (typeof document === \"undefined\") return;\n const handler = () => setVisible(document.visibilityState === \"visible\");\n document.addEventListener(\"visibilitychange\", handler);\n return () => document.removeEventListener(\"visibilitychange\", handler);\n }, []);\n\n return visible;\n}\n","import { useEffect, useState } from \"react\";\nimport { useDocVisible } from \"./useDocVisible\";\n\nexport function useIdleVisibility(timeout = 60000) {\n const visible = useDocVisible();\n const [idle, setIdle] = useState(false);\n\n useEffect(() => {\n let timer: any;\n\n const reset = () => {\n setIdle(false);\n clearTimeout(timer);\n timer = setTimeout(() => setIdle(true), timeout);\n };\n\n window.addEventListener(\"mousemove\", reset);\n window.addEventListener(\"keydown\", reset);\n reset();\n\n return () => {\n window.removeEventListener(\"mousemove\", reset);\n window.removeEventListener(\"keydown\", reset);\n clearTimeout(timer);\n };\n }, [timeout]);\n\n return { visible, idle };\n}\n","import { useEffect } from \"react\";\nimport { useDocVisible } from \"./useDocVisible\";\n\nexport function useAutoPauseVideo(ref: React.RefObject<HTMLVideoElement>) {\n const visible = useDocVisible();\n\n useEffect(() => {\n const video = ref.current;\n if (!video) return;\n\n if (!visible) video.pause();\n else video.play().catch(() => {});\n }, [visible, ref]);\n}\n","import { useState, useEffect, useRef, useCallback } from \"react\";\nimport { useDocVisible } from \"./useDocVisible\";\n\ninterface SmartPollingOptions {\n interval?: number;\n enabled?: boolean;\n}\n\ninterface SmartPollingResult<T> {\n data: T | undefined;\n isLoading: boolean;\n isError: boolean;\n error: Error | undefined;\n refetch: () => void;\n}\n\nexport function useSmartPolling<T = any>(\n fetchFn: () => Promise<T>,\n options?: SmartPollingOptions,\n): SmartPollingResult<T> {\n const { interval = 5000, enabled = true } = options ?? {};\n const visible = useDocVisible();\n const [data, setData] = useState<T | undefined>(undefined);\n const [isLoading, setIsLoading] = useState(true);\n const [error, setError] = useState<Error | undefined>(undefined);\n const fetchRef = useRef(fetchFn);\n const timerRef = useRef<ReturnType<typeof setInterval> | undefined>(\n undefined,\n );\n\n fetchRef.current = fetchFn;\n\n const execute = useCallback(async () => {\n try {\n const result = await fetchRef.current();\n setData(result);\n setError(undefined);\n } catch (err) {\n setError(err instanceof Error ? err : new Error(String(err)));\n } finally {\n setIsLoading(false);\n }\n }, []);\n\n // Initial fetch\n useEffect(() => {\n if (!enabled) return;\n
|
|
1
|
+
{"version":3,"sources":["../src/useDocVisible.ts","../src/useIdleVisibility.ts","../src/useAutoPauseVideo.ts","../src/useSmartPolling.ts"],"sourcesContent":["import { useEffect, useState } from \"react\";\n\nexport function useDocVisible(): boolean {\n const [visible, setVisible] = useState(\n typeof document === \"undefined\" || document.visibilityState === \"visible\"\n );\n\n useEffect(() => {\n if (typeof document === \"undefined\") return;\n const handler = () => setVisible(document.visibilityState === \"visible\");\n document.addEventListener(\"visibilitychange\", handler);\n return () => document.removeEventListener(\"visibilitychange\", handler);\n }, []);\n\n return visible;\n}\n","import { useEffect, useState } from \"react\";\nimport { useDocVisible } from \"./useDocVisible\";\n\nexport function useIdleVisibility(timeout = 60000) {\n const visible = useDocVisible();\n const [idle, setIdle] = useState(false);\n\n useEffect(() => {\n let timer: any;\n\n const reset = () => {\n setIdle(false);\n clearTimeout(timer);\n timer = setTimeout(() => setIdle(true), timeout);\n };\n\n window.addEventListener(\"mousemove\", reset);\n window.addEventListener(\"keydown\", reset);\n reset();\n\n return () => {\n window.removeEventListener(\"mousemove\", reset);\n window.removeEventListener(\"keydown\", reset);\n clearTimeout(timer);\n };\n }, [timeout]);\n\n return { visible, idle };\n}\n","import { useEffect } from \"react\";\nimport { useDocVisible } from \"./useDocVisible\";\n\nexport function useAutoPauseVideo(ref: React.RefObject<HTMLVideoElement>) {\n const visible = useDocVisible();\n\n useEffect(() => {\n const video = ref.current;\n if (!video) return;\n\n if (!visible) video.pause();\n else video.play().catch(() => {});\n }, [visible, ref]);\n}\n","import { useState, useEffect, useRef, useCallback } from \"react\";\nimport { useDocVisible } from \"./useDocVisible\";\n\ninterface SmartPollingOptions {\n interval?: number;\n enabled?: boolean;\n}\n\ninterface SmartPollingResult<T> {\n data: T | undefined;\n isLoading: boolean;\n isError: boolean;\n error: Error | undefined;\n refetch: () => void;\n}\n\nexport function useSmartPolling<T = any>(\n fetchFn: () => Promise<T>,\n options?: SmartPollingOptions,\n): SmartPollingResult<T> {\n const { interval = 5000, enabled = true } = options ?? {};\n const visible = useDocVisible();\n const [data, setData] = useState<T | undefined>(undefined);\n const [isLoading, setIsLoading] = useState(true);\n const [error, setError] = useState<Error | undefined>(undefined);\n const fetchRef = useRef(fetchFn);\n const timerRef = useRef<ReturnType<typeof setInterval> | undefined>(\n undefined,\n );\n const isFetchingRef = useRef(false);\n const lastJsonRef = useRef<string | undefined>(undefined);\n\n fetchRef.current = fetchFn;\n\n const execute = useCallback(async () => {\n // Prevent concurrent fetches from overlapping intervals\n if (isFetchingRef.current) return;\n isFetchingRef.current = true;\n\n try {\n const result = await fetchRef.current();\n\n // Only update state if data actually changed (avoids unnecessary re-renders)\n const json = JSON.stringify(result);\n if (json !== lastJsonRef.current) {\n lastJsonRef.current = json;\n setData(result);\n }\n setError((prev) => (prev !== undefined ? undefined : prev));\n } catch (err) {\n setError(err instanceof Error ? err : new Error(String(err)));\n } finally {\n isFetchingRef.current = false;\n setIsLoading((prev) => (prev ? false : prev));\n }\n }, []);\n\n // Initial fetch\n useEffect(() => {\n if (!enabled) return;\n execute();\n }, [enabled, execute]);\n\n // Polling — only when visible and enabled\n useEffect(() => {\n if (!enabled || !visible) {\n clearInterval(timerRef.current);\n return;\n }\n\n timerRef.current = setInterval(execute, interval);\n return () => clearInterval(timerRef.current);\n }, [visible, enabled, interval, execute]);\n\n return { data, isLoading, isError: !!error, error, refetch: execute };\n}\n"],"mappings":"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","names":["useEffect","useState","useDocVisible","visible","setVisible","handler","useEffect","useState","useIdleVisibility","timeout","visible","useDocVisible","idle","setIdle","useState","useEffect","timer","reset","useEffect","useAutoPauseVideo","ref","visible","useDocVisible","useEffect","video","useState","useEffect","useRef","useCallback","useSmartPolling","fetchFn","options","interval","enabled","visible","useDocVisible","data","setData","useState","isLoading","setIsLoading","error","setError","fetchRef","useRef","timerRef","isFetchingRef","lastJsonRef","execute","useCallback","result","json","prev","err","useEffect"]}
|