From 2128951aca120c9c2ef6a37376d72d313681e71d Mon Sep 17 00:00:00 2001 From: logonoff Date: Thu, 25 Jun 2026 14:21:34 -0400 Subject: [PATCH 1/2] 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 DetectContext to prevent it from persisting and re-triggering the effect. Adapted from PR #16410 by logonoff after files were merged into DetectContext. Co-Authored-By: Claude Opus 4.6 (1M context) --- .../components/detect-context/DetectContext.tsx | 17 +++++++++++++---- .../__tests__/DetectContext.spec.tsx | 3 ++- .../useValuesForPerspectiveContext.ts | 8 +++++--- 3 files changed, 20 insertions(+), 8 deletions(-) diff --git a/frontend/packages/console-app/src/components/detect-context/DetectContext.tsx b/frontend/packages/console-app/src/components/detect-context/DetectContext.tsx index 990432f29c4..905a36693dc 100644 --- a/frontend/packages/console-app/src/components/detect-context/DetectContext.tsx +++ b/frontend/packages/console-app/src/components/detect-context/DetectContext.tsx @@ -14,7 +14,7 @@ import { PageSidebarBody, } from '@patternfly/react-core'; import { useTranslation } from 'react-i18next'; -import { createPath, useLocation } from 'react-router'; +import { createPath, useLocation, useNavigate } from 'react-router'; import type { Perspective, ReduxReducer, ContextProvider } from '@console/dynamic-plugin-sdk'; import { PerspectiveContext, @@ -168,12 +168,21 @@ export const DetectContext: FC<{ children: ReactNode }> = ({ 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]); useEffect(() => { if (reducersResolved) { diff --git a/frontend/packages/console-app/src/components/detect-context/__tests__/DetectContext.spec.tsx b/frontend/packages/console-app/src/components/detect-context/__tests__/DetectContext.spec.tsx index 96c867bd785..4ad52bcbba4 100644 --- a/frontend/packages/console-app/src/components/detect-context/__tests__/DetectContext.spec.tsx +++ b/frontend/packages/console-app/src/components/detect-context/__tests__/DetectContext.spec.tsx @@ -49,7 +49,8 @@ jest.mock('@console/internal/redux', () => ({ jest.mock('react-router', () => ({ useLocation: jest.fn(), - createPath: jest.fn((loc) => loc.pathname), + useNavigate: jest.fn(() => jest.fn()), + createPath: jest.fn((loc) => `${loc.pathname}${loc.search || ''}${loc.hash || ''}`), })); const useValuesForPerspectiveContextMock = useValuesForPerspectiveContext as jest.Mock; diff --git a/frontend/packages/console-app/src/components/detect-context/useValuesForPerspectiveContext.ts b/frontend/packages/console-app/src/components/detect-context/useValuesForPerspectiveContext.ts index 4be72bae345..8039658f82d 100644 --- a/frontend/packages/console-app/src/components/detect-context/useValuesForPerspectiveContext.ts +++ b/frontend/packages/console-app/src/components/detect-context/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], From 4c0b96ee2500de22f9303115006f297e23c1af81 Mon Sep 17 00:00:00 2001 From: Jon Jackson Date: Thu, 25 Jun 2026 15:59:48 -0400 Subject: [PATCH 2/2] OCPBUGS-85121: Navigate to landing page when switching perspectives Previous fix prevented redundant navigations but broke perspective switching. When user switches perspective via UI, setPerspective gets called with no next path. Code skipped navigate entirely, preventing landing page redirect. Now navigate to '/' when perspective changes and no explicit path provided. Router handles landing page redirect. Still skip navigate when perspective unchanged to avoid redundant reloads. Co-Authored-By: Claude Sonnet 4.5 --- .../detect-context/useValuesForPerspectiveContext.ts | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/frontend/packages/console-app/src/components/detect-context/useValuesForPerspectiveContext.ts b/frontend/packages/console-app/src/components/detect-context/useValuesForPerspectiveContext.ts index 8039658f82d..fffb3dab069 100644 --- a/frontend/packages/console-app/src/components/detect-context/useValuesForPerspectiveContext.ts +++ b/frontend/packages/console-app/src/components/detect-context/useValuesForPerspectiveContext.ts @@ -36,15 +36,19 @@ export const useValuesForPerspectiveContext = (): [ const setPerspective = useCallback( (newPerspective, next) => { + const perspectiveChanged = newPerspective !== perspective; setLastPerspective(newPerspective); setActivePerspective(newPerspective); - // Only navigate if a path is provided to avoid unnecessary navigation - if (next && next !== createPath(window.location)) { - navigate(next); + // Only navigate if perspective changed + if (perspectiveChanged) { + const targetPath = next || '/'; + if (targetPath !== createPath(window.location)) { + navigate(targetPath); + } } fireTelemetryEvent('Perspective Changed', { perspective: newPerspective }); }, - [setLastPerspective, setActivePerspective, navigate, fireTelemetryEvent], + [setLastPerspective, setActivePerspective, navigate, fireTelemetryEvent, perspective], ); return [isValidPerspective ? perspective : '', setPerspective, loaded];