From 525e3e38d0649f1dcdebb03b2cb8fcf550308062 Mon Sep 17 00:00:00 2001 From: Bytechoreographer Date: Thu, 16 Apr 2026 15:17:00 +0800 Subject: [PATCH] fix(ui): VirtualKeysPage infinite re-render loop on Virtual Keys page MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit keys?.keys || [] creates a fresh [] reference on every render, which triggers useFilterLogic's useEffect (dep: keys), which calls setFilteredKeys, which re-renders the parent, which creates another new [] — maximum update depth exceeded. Stabilize the reference with useMemo(() => keys?.keys ?? [], [keys]) so the effect only fires when the actual server data changes. Co-Authored-By: Claude Sonnet 4 (1M context) --- .../src/components/VirtualKeysPage/VirtualKeysTable.tsx | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/ui/litellm-dashboard/src/components/VirtualKeysPage/VirtualKeysTable.tsx b/ui/litellm-dashboard/src/components/VirtualKeysPage/VirtualKeysTable.tsx index b30d4b6ce5b..e4d9d7b3ac4 100644 --- a/ui/litellm-dashboard/src/components/VirtualKeysPage/VirtualKeysTable.tsx +++ b/ui/litellm-dashboard/src/components/VirtualKeysPage/VirtualKeysTable.tsx @@ -94,11 +94,15 @@ export function VirtualKeysTable({ teams, organizations, onSortChange, currentSo }); const [expandedAccordions, setExpandedAccordions] = useState>({}); - // Use the filter logic hook + // Stabilize the keys array reference so that useFilterLogic's useEffect + // (which depends on `keys`) only fires when the actual data changes, not on + // every render caused by setFilteredKeys → re-render → new [] literal. + const stableKeys = useMemo(() => keys?.keys ?? [], [keys]); + // Use the filter logic hook const { filters, filteredKeys, filteredTotalCount, allTeams, allOrganizations, handleFilterChange, handleFilterReset } = useFilterLogic({ - keys: keys?.keys || [], + keys: stableKeys, teams, organizations, });