From bbd3ece46aa02cdaeed0ab6a59b061d1bcfb14ff Mon Sep 17 00:00:00 2001 From: logonoff Date: Thu, 7 May 2026 13:11:14 -0400 Subject: [PATCH] OCPBUGS-85121: Skip redundant navigate() in setPerspective when target matches current URL setActivePerspective() always called navigate() unconditionally, causing redundant page reloads for custom perspective plugins. When the target URL matched the current location, this triggered namespace handler validation loops and visible re-renders. This also stripped the ?perspective= param in DetectPerspective to prevent it from persisting and re-triggering the effect. Co-Authored-By: Claude Opus 4.6 (1M context) --- .../detect-perspective/DetectPerspective.tsx | 17 +++++++++++++---- .../__tests__/DetectPerspective.spec.tsx | 2 ++ .../useValuesForPerspectiveContext.ts | 8 +++++--- 3 files changed, 20 insertions(+), 7 deletions(-) diff --git a/frontend/packages/console-app/src/components/detect-perspective/DetectPerspective.tsx b/frontend/packages/console-app/src/components/detect-perspective/DetectPerspective.tsx index 94c1738d443..6872e1d9656 100644 --- a/frontend/packages/console-app/src/components/detect-perspective/DetectPerspective.tsx +++ b/frontend/packages/console-app/src/components/detect-perspective/DetectPerspective.tsx @@ -1,6 +1,6 @@ import type { FC } from 'react'; import { useEffect } from 'react'; -import { createPath, useLocation } from 'react-router'; +import { createPath, useLocation, useNavigate } from 'react-router'; import type { Perspective } from '@console/dynamic-plugin-sdk'; import { PerspectiveContext } from '@console/dynamic-plugin-sdk'; import { LoadingBox } from '@console/shared/src/components/loading/LoadingBox'; @@ -27,11 +27,20 @@ const DetectPerspective: FC = ({ children }) => { const perspectiveExtensions = usePerspectives(); const perspectiveParam = getPerspectiveURLParam(perspectiveExtensions); const location = useLocation(); + const navigate = useNavigate(); useEffect(() => { - if (perspectiveParam && perspectiveParam !== activePerspective) { - setActivePerspective(perspectiveParam, createPath(location)); + if (perspectiveParam) { + const params = new URLSearchParams(location.search); + params.delete('perspective'); + const search = params.toString(); + const cleanPath = createPath({ ...location, search: search ? `?${search}` : '' }); + if (perspectiveParam !== activePerspective) { + setActivePerspective(perspectiveParam, cleanPath); + } else { + navigate(cleanPath, { replace: true }); + } } - }, [perspectiveParam, activePerspective, setActivePerspective, location]); + }, [perspectiveParam, activePerspective, setActivePerspective, navigate, location]); return loaded ? ( activePerspective ? ( diff --git a/frontend/packages/console-app/src/components/detect-perspective/__tests__/DetectPerspective.spec.tsx b/frontend/packages/console-app/src/components/detect-perspective/__tests__/DetectPerspective.spec.tsx index 3fff46721b4..8dcb933ea75 100644 --- a/frontend/packages/console-app/src/components/detect-perspective/__tests__/DetectPerspective.spec.tsx +++ b/frontend/packages/console-app/src/components/detect-perspective/__tests__/DetectPerspective.spec.tsx @@ -20,7 +20,9 @@ jest.mock('@console/shared/src/hooks/usePerspectives', () => ({ })); jest.mock('react-router', () => ({ + createPath: jest.fn((loc) => `${loc.pathname}${loc.search || ''}${loc.hash || ''}`), useLocation: jest.fn(), + useNavigate: jest.fn(() => jest.fn()), })); const useValuesForPerspectiveContextMock = useValuesForPerspectiveContext as jest.Mock; diff --git a/frontend/packages/console-app/src/components/detect-perspective/useValuesForPerspectiveContext.ts b/frontend/packages/console-app/src/components/detect-perspective/useValuesForPerspectiveContext.ts index 4be72bae345..8039658f82d 100644 --- a/frontend/packages/console-app/src/components/detect-perspective/useValuesForPerspectiveContext.ts +++ b/frontend/packages/console-app/src/components/detect-perspective/useValuesForPerspectiveContext.ts @@ -1,5 +1,5 @@ import { useCallback, useState } from 'react'; -import { useNavigate } from 'react-router'; +import { createPath, useNavigate } from 'react-router'; import type { PerspectiveType, UseActivePerspective } from '@console/dynamic-plugin-sdk'; import { usePerspectiveExtension, @@ -38,8 +38,10 @@ export const useValuesForPerspectiveContext = (): [ (newPerspective, next) => { setLastPerspective(newPerspective); setActivePerspective(newPerspective); - // Navigate to next or root and let the default page determine where to go to next - navigate(next || '/'); + // Only navigate if a path is provided to avoid unnecessary navigation + if (next && next !== createPath(window.location)) { + navigate(next); + } fireTelemetryEvent('Perspective Changed', { perspective: newPerspective }); }, [setLastPerspective, setActivePerspective, navigate, fireTelemetryEvent],