@@ -10,6 +10,7 @@ import {
1010 type QueryWidgetConfig ,
1111 type QueryWidgetData ,
1212} from "~/components/metrics/QueryWidget" ;
13+ import { useElementVisibility } from "~/hooks/useElementVisibility" ;
1314import { useInterval } from "~/hooks/useInterval" ;
1415
1516const Scope = z . union ( [ z . literal ( "environment" ) , z . literal ( "organization" ) , z . literal ( "project" ) ] ) ;
@@ -178,6 +179,9 @@ export function MetricWidget({
178179 propsRef . current = props ;
179180
180181 const submit = useCallback ( ( ) => {
182+ // Skip fetching if the widget is not visible on screen
183+ if ( ! isVisibleRef . current ) return ;
184+
181185 // Abort any in-flight request for this widget
182186 abortControllerRef . current ?. abort ( ) ;
183187
@@ -209,15 +213,23 @@ export function MetricWidget({
209213 if ( ! controller . signal . aborted ) {
210214 // Only surface the error if there's no existing successful data to preserve
211215 setResponse ( ( prev ) =>
212- prev ?. success
213- ? prev
214- : { success : false , error : err . message || "Network error" }
216+ prev ?. success ? prev : { success : false , error : err . message || "Network error" }
215217 ) ;
216218 setIsLoading ( false ) ;
217219 }
218220 } ) ;
219221 } , [ ] ) ;
220222
223+ // Track visibility so we only fetch for on-screen widgets.
224+ // When a widget scrolls into view and has no data yet, trigger a load.
225+ const { ref : visibilityRef , isVisibleRef } = useElementVisibility ( {
226+ onVisibilityChange : ( visible ) => {
227+ if ( visible && ! response ) {
228+ submit ( ) ;
229+ }
230+ } ,
231+ } ) ;
232+
221233 // Clean up on unmount
222234 useEffect ( ( ) => {
223235 return ( ) => {
@@ -249,21 +261,23 @@ export function MetricWidget({
249261 const timeRange = response ?. success ? response . data . timeRange : undefined ;
250262
251263 return (
252- < QueryWidget
253- title = { title }
254- titleString = { title }
255- query = { props . query }
256- config = { config }
257- isLoading = { isLoading }
258- data = { data }
259- timeRange = { timeRange }
260- error = { response ?. success === false ? response . error : undefined }
261- isResizing = { isResizing }
262- isDraggable = { isDraggable }
263- onEdit = { onEdit }
264- onRename = { onRename }
265- onDelete = { onDelete }
266- onDuplicate = { onDuplicate }
267- />
264+ < div ref = { visibilityRef } className = "h-full" >
265+ < QueryWidget
266+ title = { title }
267+ titleString = { title }
268+ query = { props . query }
269+ config = { config }
270+ isLoading = { isLoading }
271+ data = { data }
272+ timeRange = { timeRange }
273+ error = { response ?. success === false ? response . error : undefined }
274+ isResizing = { isResizing }
275+ isDraggable = { isDraggable }
276+ onEdit = { onEdit }
277+ onRename = { onRename }
278+ onDelete = { onDelete }
279+ onDuplicate = { onDuplicate }
280+ />
281+ </ div >
268282 ) ;
269283}
0 commit comments