mirror of
https://github.com/blakeblackshear/frigate.git
synced 2026-03-28 02:58:22 +03:00
fix React 19 infinite re-render loop on live dashboard
The "Maximum update depth exceeded" error was caused by two issues:
1. useDeferredStreamMetadata returned a new `{}` default on every render
when SWR data was undefined, creating an unstable reference that
triggered the useEffect in useCameraLiveMode on every render cycle.
Fixed by using a stable module-level EMPTY_METADATA constant.
2. useResizeObserver's rest parameter `...refs` created a new array on
every render, causing its useEffect to re-run and re-observe elements
continuously. Fixed by stabilizing refs with useRef and only
reconnecting the observer when actual DOM elements change.
This commit is contained in:
parent
4e37035d87
commit
93889b221c
@ -1,4 +1,4 @@
|
|||||||
import { MutableRefObject, useEffect, useMemo, useState } from "react";
|
import { MutableRefObject, useEffect, useMemo, useRef, useState } from "react";
|
||||||
|
|
||||||
type RefType = MutableRefObject<Element | null> | Window;
|
type RefType = MutableRefObject<Element | null> | 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(() => {
|
useEffect(() => {
|
||||||
refs.forEach((ref) => {
|
const currentRefs = stableRefs.current;
|
||||||
if (ref instanceof Window) {
|
const elements = currentRefs.map((ref) =>
|
||||||
resizeObserver.observe(document.body);
|
ref instanceof Window ? document.body : ref.current,
|
||||||
} else if (ref.current) {
|
);
|
||||||
resizeObserver.observe(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 () => {
|
return () => {
|
||||||
refs.forEach((ref) => {
|
resizeObserver.disconnect();
|
||||||
if (ref instanceof Window) {
|
|
||||||
resizeObserver.unobserve(document.body);
|
|
||||||
} else if (ref.current) {
|
|
||||||
resizeObserver.unobserve(ref.current);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
}, [refs, resizeObserver]);
|
});
|
||||||
|
|
||||||
if (dimensions.length == refs.length) {
|
if (dimensions.length == refs.length) {
|
||||||
return dimensions;
|
return dimensions;
|
||||||
|
|||||||
@ -5,6 +5,7 @@ import { LiveStreamMetadata } from "@/types/live";
|
|||||||
|
|
||||||
const FETCH_TIMEOUT_MS = 10000;
|
const FETCH_TIMEOUT_MS = 10000;
|
||||||
const DEFER_DELAY_MS = 2000;
|
const DEFER_DELAY_MS = 2000;
|
||||||
|
const EMPTY_METADATA: { [key: string]: LiveStreamMetadata } = {};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Hook that fetches go2rtc stream metadata with deferred loading.
|
* Hook that fetches go2rtc stream metadata with deferred loading.
|
||||||
@ -77,7 +78,7 @@ export default function useDeferredStreamMetadata(streamNames: string[]) {
|
|||||||
return metadata;
|
return metadata;
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const { data: metadata = {} } = useSWR<{
|
const { data: metadata = EMPTY_METADATA } = useSWR<{
|
||||||
[key: string]: LiveStreamMetadata;
|
[key: string]: LiveStreamMetadata;
|
||||||
}>(swrKey, fetcher, {
|
}>(swrKey, fetcher, {
|
||||||
revalidateOnFocus: false,
|
revalidateOnFocus: false,
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user