From 18c9259d00889352cc2f37c3565d9ab08d752325 Mon Sep 17 00:00:00 2001 From: Yuneng Jiang Date: Sat, 11 Jul 2026 10:38:00 -0700 Subject: [PATCH] refactor(ui): use TanStack Pacer debounce for the team keys search Replace lodash/debounce in TeamVirtualKeysTable with useDebouncedValue from @tanstack/react-pacer, matching the sibling VirtualKeysTable and PaginatedKeyAliasSelect which already debounce their key-alias search that way. Pacer is already a dependency, so this drops the odd-one-out lodash usage and keeps the search-debounce pattern consistent across the key tables. --- .../team/TeamVirtualKeysTable.test.tsx | 11 +++++---- .../components/team/TeamVirtualKeysTable.tsx | 24 +++++-------------- 2 files changed, 13 insertions(+), 22 deletions(-) diff --git a/ui/litellm-dashboard/src/components/team/TeamVirtualKeysTable.test.tsx b/ui/litellm-dashboard/src/components/team/TeamVirtualKeysTable.test.tsx index ec6e97a1d23..1c9b14d0c9d 100644 --- a/ui/litellm-dashboard/src/components/team/TeamVirtualKeysTable.test.tsx +++ b/ui/litellm-dashboard/src/components/team/TeamVirtualKeysTable.test.tsx @@ -24,6 +24,11 @@ vi.mock("../templates/key_info_view", () => ({ )), })); +// Resolve the debounced search synchronously so typed input lands in the useKeys query within the test tick. +vi.mock("@tanstack/react-pacer/debouncer", () => ({ + useDebouncedValue: (value: unknown) => [value, { cancel: vi.fn(), flush: vi.fn() }], +})); + const mockUseKeys = useKeys as MockedFunction; const createMockKey = (overrides: Partial = {}): KeyResponse => @@ -286,10 +291,8 @@ describe("TeamVirtualKeysTable", () => { await user.type(await screen.findByTestId("datatable-search"), "check-002"); - await waitFor( - () => - expect(mockUseKeys).toHaveBeenLastCalledWith(1, 50, expect.objectContaining({ selectedKeyAlias: "check-002" })), - { timeout: 2000 }, + await waitFor(() => + expect(mockUseKeys).toHaveBeenLastCalledWith(1, 50, expect.objectContaining({ selectedKeyAlias: "check-002" })), ); }); diff --git a/ui/litellm-dashboard/src/components/team/TeamVirtualKeysTable.tsx b/ui/litellm-dashboard/src/components/team/TeamVirtualKeysTable.tsx index e008de12dd4..207e6f2ccfe 100644 --- a/ui/litellm-dashboard/src/components/team/TeamVirtualKeysTable.tsx +++ b/ui/litellm-dashboard/src/components/team/TeamVirtualKeysTable.tsx @@ -10,10 +10,10 @@ import { } from "@/components/shared/DataTable"; import { Input } from "@/components/ui/input"; import { ChevronDownIcon, ChevronRightIcon } from "@heroicons/react/outline"; +import { useDebouncedValue } from "@tanstack/react-pacer/debouncer"; import { ColumnDef, ColumnFiltersState, OnChangeFn, PaginationState, SortingState } from "@tanstack/react-table"; import { Badge, Icon, Text } from "@tremor/react"; import { Popover, Tooltip, Typography } from "antd"; -import debounce from "lodash/debounce"; import DefaultProxyAdminTag from "../common_components/DefaultProxyAdminTag"; import React, { useCallback, useEffect, useMemo, useState } from "react"; import { getModelDisplayName } from "../key_team_helpers/fetch_available_models_team_key"; @@ -43,24 +43,12 @@ export function TeamVirtualKeysTable({ teamId, teamAlias, organization }: TeamVi const [columnFilters, setColumnFilters] = useState([]); const [filtersOpen, setFiltersOpen] = useState(false); const [searchInput, setSearchInput] = useState(""); - const [searchQuery, setSearchQuery] = useState(""); + const [searchQuery] = useDebouncedValue(searchInput, { wait: 300 }); - const debouncedSetSearch = useMemo( - () => - debounce((value: string) => { - setSearchQuery(value); - setTablePagination((prev) => ({ ...prev, pageIndex: 0 })); - }, 300), - [], - ); - useEffect(() => () => debouncedSetSearch.cancel(), [debouncedSetSearch]); - const handleSearchChange = useCallback( - (value: string) => { - setSearchInput(value); - debouncedSetSearch(value); - }, - [debouncedSetSearch], - ); + const handleSearchChange = useCallback((value: string) => { + setSearchInput(value); + setTablePagination((prev) => ({ ...prev, pageIndex: 0 })); + }, []); const getFilterValue = useCallback( (columnId: string): string | undefined => {