diff --git a/web/src/hooks/resize-observer.ts b/web/src/hooks/resize-observer.ts index 1e174af7e..a4c52e4f2 100644 --- a/web/src/hooks/resize-observer.ts +++ b/web/src/hooks/resize-observer.ts @@ -1,4 +1,4 @@ -import { MutableRefObject, useEffect, useMemo, useState } from "react"; +import { MutableRefObject, useEffect, useMemo, useRef, useState } from "react"; type RefType = MutableRefObject | Window; @@ -31,25 +31,39 @@ export function useResizeObserver(...refs: RefType[]) { [], ); + // Stabilize refs array to prevent useEffect from re-running on every render + // (rest params create a new array each call) + const stableRefs = useRef(refs); + stableRefs.current = refs; + + // Track observed elements to detect actual changes + const observedElements = useRef<(Element | null)[]>([]); + useEffect(() => { - refs.forEach((ref) => { - if (ref instanceof Window) { - resizeObserver.observe(document.body); - } else if (ref.current) { - resizeObserver.observe(ref.current); - } + const currentRefs = stableRefs.current; + const elements = currentRefs.map((ref) => + ref instanceof Window ? document.body : ref.current, + ); + + // Only re-observe if actual DOM elements changed + const prevElements = observedElements.current; + const changed = + elements.length !== prevElements.length || + elements.some((el, i) => el !== prevElements[i]); + + if (!changed) return; + + // Disconnect all and re-observe + resizeObserver.disconnect(); + elements.forEach((el) => { + if (el) resizeObserver.observe(el); }); + observedElements.current = elements; return () => { - refs.forEach((ref) => { - if (ref instanceof Window) { - resizeObserver.unobserve(document.body); - } else if (ref.current) { - resizeObserver.unobserve(ref.current); - } - }); + resizeObserver.disconnect(); }; - }, [refs, resizeObserver]); + }); if (dimensions.length == refs.length) { return dimensions; diff --git a/web/src/hooks/use-deferred-stream-metadata.ts b/web/src/hooks/use-deferred-stream-metadata.ts index 8e68b6a6a..44bd1bb0c 100644 --- a/web/src/hooks/use-deferred-stream-metadata.ts +++ b/web/src/hooks/use-deferred-stream-metadata.ts @@ -5,6 +5,7 @@ import { LiveStreamMetadata } from "@/types/live"; const FETCH_TIMEOUT_MS = 10000; const DEFER_DELAY_MS = 2000; +const EMPTY_METADATA: { [key: string]: LiveStreamMetadata } = {}; /** * Hook that fetches go2rtc stream metadata with deferred loading. @@ -77,7 +78,7 @@ export default function useDeferredStreamMetadata(streamNames: string[]) { return metadata; }, []); - const { data: metadata = {} } = useSWR<{ + const { data: metadata = EMPTY_METADATA } = useSWR<{ [key: string]: LiveStreamMetadata; }>(swrKey, fetcher, { revalidateOnFocus: false,