mirror of
https://github.com/blakeblackshear/frigate.git
synced 2026-03-19 22: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
f66af071d6
@ -31,25 +31,24 @@ export function useResizeObserver(...refs: RefType[]) {
|
|||||||
[],
|
[],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Resolve refs to actual DOM elements for use as stable effect dependencies.
|
||||||
|
// Rest params create a new array each call, but the underlying elements are
|
||||||
|
// stable DOM nodes, so spreading them into the dep array avoids re-running
|
||||||
|
// the effect on every render.
|
||||||
|
const elements = refs.map((ref) =>
|
||||||
|
ref instanceof Window ? document.body : ref.current,
|
||||||
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
refs.forEach((ref) => {
|
elements.forEach((el) => {
|
||||||
if (ref instanceof Window) {
|
if (el) resizeObserver.observe(el);
|
||||||
resizeObserver.observe(document.body);
|
|
||||||
} else if (ref.current) {
|
|
||||||
resizeObserver.observe(ref.current);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
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]);
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
}, [...elements, 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