From 046d466e164d2591bf93bea027fd1a2cca4ba5f0 Mon Sep 17 00:00:00 2001 From: michelia Date: Tue, 14 Jul 2026 13:29:15 +0800 Subject: [PATCH] fix(usage): memoize resource breakdown filters (review comment) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The GPU Instances / Storage tabs built the filter object inline (in baseRequest and again in the extra tab's props), duplicating the conditional-spread logic and handing the enterprise Organization tab a fresh object every render — which refired its fetch effect on any state change. Hoist it into one memoized ``breakdownFilters`` shared by the chart request and the extra tab, mirroring the Tokens tab's stable-filters shape. --- src/pages/usage/instances-tab/index.tsx | 59 +++++++++++-------------- src/pages/usage/storage-tab/index.tsx | 52 +++++++++------------- 2 files changed, 49 insertions(+), 62 deletions(-) diff --git a/src/pages/usage/instances-tab/index.tsx b/src/pages/usage/instances-tab/index.tsx index e83ddc32..8ce55ab2 100644 --- a/src/pages/usage/instances-tab/index.tsx +++ b/src/pages/usage/instances-tab/index.tsx @@ -164,29 +164,37 @@ const GpuInstancesTab: React.FC = () => { fetchData: fetchChartData } = useQueryGpuInstancesBreakdown({ key: 'gpuInstancesBreakdownChart' }); + // Single source of truth for the active filter set — memoized so its + // reference only changes when a selection changes (an inline object would + // refire the extra tab's fetch effect on every render), and shared by both + // the chart request and the enterprise extra tab (no duplicated spread). + const breakdownFilters = useMemo( + () => ({ + ...(selectedUsers.length ? { creator_ids: selectedUsers } : {}), + ...(selectedInstances.length ? { instance_ids: selectedInstances } : {}), + ...(selectedOrganizations.length + ? { organization_ids: selectedOrganizations } + : {}), + ...(selectedUserGroups.length + ? { user_group_ids: selectedUserGroups } + : {}) + }), + [ + selectedUsers, + selectedInstances, + selectedOrganizations, + selectedUserGroups + ] + ); + const baseRequest = (): Omit => ({ start_date: dateRange[0].format('YYYY-MM-DD'), end_date: dateRange[1].format('YYYY-MM-DD'), scope, granularity, - filters: - selectedUsers.length || - selectedInstances.length || - selectedOrganizations.length || - selectedUserGroups.length - ? { - ...(selectedUsers.length ? { creator_ids: selectedUsers } : {}), - ...(selectedInstances.length - ? { instance_ids: selectedInstances } - : {}), - ...(selectedOrganizations.length - ? { organization_ids: selectedOrganizations } - : {}), - ...(selectedUserGroups.length - ? { user_group_ids: selectedUserGroups } - : {}) - } - : undefined, + filters: Object.keys(breakdownFilters).length + ? breakdownFilters + : undefined, page: 1, perPage: 50 }); @@ -536,20 +544,7 @@ const GpuInstancesTab: React.FC = () => { tab="gpu-instances" dateRange={dateRange} scope={scope} - filters={{ - ...(selectedUsers.length - ? { creator_ids: selectedUsers } - : {}), - ...(selectedInstances.length - ? { instance_ids: selectedInstances } - : {}), - ...(selectedOrganizations.length - ? { organization_ids: selectedOrganizations } - : {}), - ...(selectedUserGroups.length - ? { user_group_ids: selectedUserGroups } - : {}) - }} + filters={breakdownFilters} pageResetKey={pageResetKey} refreshKey={refreshKey} /> diff --git a/src/pages/usage/storage-tab/index.tsx b/src/pages/usage/storage-tab/index.tsx index ca64dd06..3b0fa8be 100644 --- a/src/pages/usage/storage-tab/index.tsx +++ b/src/pages/usage/storage-tab/index.tsx @@ -158,27 +158,32 @@ const StorageTab: React.FC = () => { fetchData: fetchChartData } = useQueryStorageBreakdown({ key: 'storageBreakdownChart' }); + // Single source of truth for the active filter set — memoized so its + // reference only changes when a selection changes (an inline object would + // refire the extra tab's fetch effect on every render), and shared by both + // the chart request and the enterprise extra tab (no duplicated spread). + const breakdownFilters = useMemo( + () => ({ + ...(selectedUsers.length ? { creator_ids: selectedUsers } : {}), + ...(selectedVolumes.length ? { volume_ids: selectedVolumes } : {}), + ...(selectedOrganizations.length + ? { organization_ids: selectedOrganizations } + : {}), + ...(selectedUserGroups.length + ? { user_group_ids: selectedUserGroups } + : {}) + }), + [selectedUsers, selectedVolumes, selectedOrganizations, selectedUserGroups] + ); + const baseRequest = (): Omit => ({ start_date: dateRange[0].format('YYYY-MM-DD'), end_date: dateRange[1].format('YYYY-MM-DD'), scope, granularity, - filters: - selectedUsers.length || - selectedVolumes.length || - selectedOrganizations.length || - selectedUserGroups.length - ? { - ...(selectedUsers.length ? { creator_ids: selectedUsers } : {}), - ...(selectedVolumes.length ? { volume_ids: selectedVolumes } : {}), - ...(selectedOrganizations.length - ? { organization_ids: selectedOrganizations } - : {}), - ...(selectedUserGroups.length - ? { user_group_ids: selectedUserGroups } - : {}) - } - : undefined, + filters: Object.keys(breakdownFilters).length + ? breakdownFilters + : undefined, page: 1, perPage: 50 }); @@ -516,20 +521,7 @@ const StorageTab: React.FC = () => { tab="storage" dateRange={dateRange} scope={scope} - filters={{ - ...(selectedUsers.length - ? { creator_ids: selectedUsers } - : {}), - ...(selectedVolumes.length - ? { volume_ids: selectedVolumes } - : {}), - ...(selectedOrganizations.length - ? { organization_ids: selectedOrganizations } - : {}), - ...(selectedUserGroups.length - ? { user_group_ids: selectedUserGroups } - : {}) - }} + filters={breakdownFilters} pageResetKey={pageResetKey} refreshKey={refreshKey} />