CONSOLE-4990: Replace history object navigation with useNavigate hook#15959
CONSOLE-4990: Replace history object navigation with useNavigate hook#15959rhamilto wants to merge 8 commits intoopenshift:mainfrom
Conversation
|
Skipping CI for Draft Pull Request. |
|
@rhamilto: This pull request references CONSOLE-4990 which is a valid jira issue. Warning: The referenced jira issue has an invalid target version for the target branch this PR targets: expected the story to target the "4.22.0" version, but no target version was set. DetailsIn response to this:
Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the openshift-eng/jira-lifecycle-plugin repository. |
|
[APPROVALNOTIFIER] This PR is APPROVED This pull-request has been approved by: rhamilto The full list of commands accepted by this bot can be found here. The pull request process is described here DetailsNeeds approval from an approver in each of these files:
Approvers can indicate their approval by writing |
frontend/packages/console-app/src/actions/providers/cronjob-provider.ts
Outdated
Show resolved
Hide resolved
frontend/packages/console-shared/src/components/dynamic-form/index.tsx
Outdated
Show resolved
Hide resolved
frontend/packages/console-shared/src/components/quick-search/QuickSearchContent.tsx
Outdated
Show resolved
Hide resolved
frontend/packages/console-shared/src/components/quick-search/QuickSearchDetails.tsx
Show resolved
Hide resolved
frontend/packages/dev-console/src/components/edit-application/EditApplication.tsx
Outdated
Show resolved
Hide resolved
frontend/packages/operator-lifecycle-manager/src/components/operator-hub/operator-hub-items.tsx
Outdated
Show resolved
Hide resolved
fab6845 to
4c78f4b
Compare
|
@rhamilto: This pull request references CONSOLE-4990 which is a valid jira issue. Warning: The referenced jira issue has an invalid target version for the target branch this PR targets: expected the story to target the "4.22.0" version, but no target version was set. DetailsIn response to this:
Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the openshift-eng/jira-lifecycle-plugin repository. |
738897a to
b7a45bf
Compare
|
@rhamilto: The DetailsIn response to this:
Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the openshift-eng/jira-lifecycle-plugin repository. |
|
/verified by @rhamilto |
|
@rhamilto: This PR has been marked as verified by DetailsIn response to this:
Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the openshift-eng/jira-lifecycle-plugin repository. |
|
/retest |
|
tech debt |
|
/label acknowledge-critical-fixes-only |
logonoff
left a comment
There was a problem hiding this comment.
good stuff! here are a bunch of nitpicks
| ## Migration from `history` package to React Router types | ||
|
|
||
| Console has migrated from using the `history` package to React Router's native types. This affects | ||
| the following public API types: | ||
|
|
||
| - `UseDeleteModal` hook's `redirectTo` parameter type has changed from `LocationDescriptor` (from `history`) | ||
| to `To` (from `react-router-dom-v5-compat`) | ||
|
|
||
| ### Migration guide | ||
|
|
||
| If your plugin uses the `useDeleteModal` hook and passes a `redirectTo` parameter, update your imports: | ||
|
|
||
| ```diff | ||
| - import { LocationDescriptor } from 'history'; | ||
| + import { To } from 'react-router-dom-v5-compat'; | ||
|
|
||
| const MyComponent = ({ resource }) => { | ||
| - const launchDeleteModal = useDeleteModal(resource, redirectTo as LocationDescriptor); | ||
| + const launchDeleteModal = useDeleteModal(resource, redirectTo as To); | ||
| // ... | ||
| }; | ||
| ``` | ||
|
|
||
| The `To` type is compatible with the previous `LocationDescriptor` type (accepts strings and location | ||
| objects), so most plugins should only need to update their imports. | ||
|
|
There was a problem hiding this comment.
I'd like to put this in a seperate react-router subsection given we'll likely have other stuff there
There was a problem hiding this comment.
Once the react-router upgrade is done, we'll add the following and demote the headings being added.
## React Router upgrade
Console has upgraded from react router 5 to 6. As a result, some changes to the plugin API were made. Here is how you upgrade your plugin:
### Migration from `history` package to React Router types
...
frontend/packages/console-dynamic-plugin-sdk/src/extensions/console-types.ts
Show resolved
Hide resolved
frontend/packages/console-shared/src/components/catalog/utils/catalog-utils.tsx
Outdated
Show resolved
Hide resolved
frontend/packages/console-shared/src/components/catalog/utils/catalog-utils.tsx
Outdated
Show resolved
Hide resolved
frontend/packages/dev-console/src/components/edit-application/EditApplication.tsx
Outdated
Show resolved
Hide resolved
frontend/packages/dev-console/src/components/projects/details/ProjectDetailsPage.tsx
Outdated
Show resolved
Hide resolved
frontend/packages/helm-plugin/src/components/details-page/HelmReleaseDetailsPage.tsx
Outdated
Show resolved
Hide resolved
frontend/packages/knative-plugin/src/components/functions/FunctionDetailsPage.tsx
Outdated
Show resolved
Hide resolved
frontend/packages/dev-console/src/components/import/ImportForm.tsx
Outdated
Show resolved
Hide resolved
frontend/packages/knative-plugin/src/components/knatify/CreateKnatifyPage.tsx
Outdated
Show resolved
Hide resolved
rhamilto
left a comment
There was a problem hiding this comment.
Addressed review comments:
✅ Type-only imports (comments #2817764074, #2817781098, #2817782328):
- Changed to
import type { To }in console-types.ts - Changed to
import type { NavigateFunction }in import-submit-utils.ts - Added
NavigateFunctiontype in ProjectDetailsPage.tsx
✅ DRY NavigateFunction type (comments #2817769613, #2817770687):
- Updated
setURLParamsandupdateURLParamsin catalog-utils.tsx to useNavigateFunctiontype
✅ Remove unnecessary return statements (comments #2817777547, #2817801167, #2817801364):
- Removed
returnbeforehandleRedirect()in EditApplication.tsx - Removed
returnbeforehandleRedirect()in ImportForm.tsx - Removed
returnbeforehandleRedirect()in CreateKnatifyPage.tsx
✅ useCallback optimization (comment #2817790079):
- Wrapped
handleNamespaceChangeinuseCallbackin HelmReleaseDetailsPage.tsx
✅ Use renderWithProviders (comment #2817785414):
- Updated ProjectDetailsPage.spec.tsx to use
renderWithProvidersutility
✅ Use generatePath (comment #2818935424):
- Updated FunctionDetailsPage.tsx to use
generatePathfor safer path construction
All changes have been squashed into their respective commits.
2f4ced5 to
783cf6a
Compare
|
@rhamilto: This pull request references CONSOLE-4990 which is a valid jira issue. Warning: The referenced jira issue has an invalid target version for the target branch this PR targets: expected the story to target the "4.22.0" version, but no target version was set. DetailsIn response to this:
Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the openshift-eng/jira-lifecycle-plugin repository. |
Replace history object usage with useNavigate hook in console-app package. Changes: - cronjob-factory.ts: Converted to dependency injection pattern - cronjob-provider.ts: Use navigate in action creator - ClusterConfigurationPage.tsx: Replace history.push with navigate - Lightspeed.tsx: Replace history.push with navigate - clone-pvc-modal.tsx: Replace history.push with navigate - restore-pvc-modal.tsx: Replace history.push with navigate - PDBForm.tsx: Replace history.push with navigate - UserPreferencePage.tsx: Replace history.push with navigate - create-volume-snapshot.tsx: Replace history.push with navigate All changes: - Replaced history.push(path) with navigate(path) - Added useNavigate() hook calls - Updated imports to use react-router-dom-v5-compat Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Replace history object usage with useNavigate hook in console-shared package. Changes: - ActionMenuItem.tsx: Replace history.push with navigate - CatalogTile.tsx: Replace history.push with navigate - CatalogView.tsx: Replace history.push with navigate - catalog-utils.tsx: Accept NavigateFunction as parameter - dynamic-form/index.tsx: Replace history.goBack with navigate(-1) - error-boundary.tsx: Use componentDidUpdate instead of key prop for location-based reset - DeleteResourceModal.tsx: Replace history.push with navigate - MultiTabListPage.tsx: Replace history.push with navigate Migration patterns: - Components: Added useNavigate() hook - Utilities: Parameter injection for NavigateFunction - ErrorBoundary: Pass locationPathname as prop and use componentDidUpdate to reset error state only when there's an active error AND location changes. This avoids unnecessary component remounting during tab navigation. Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Replace history object usage with useNavigate hook in dev-console package. Changes: - AddCardItem.tsx: Pass navigate to navigateTo utility - EditBuildConfig.tsx: Replace history.push with navigate - EditDeployment.tsx: Replace history.push with navigate - EditApplication.tsx: Replace history.goBack with navigate(-1) - AddHealthChecks.tsx: Replace history.push with navigate - AddHealthChecksForm.tsx: Replace history.goBack with navigate(-1) - HPAFormikForm.tsx: Replace history.goBack with navigate(-1) - DeployImage.tsx: Pass navigate to handleRedirect - ImportForm.tsx: Pass navigate to handleRedirect - ImportSamplePage.tsx: Pass navigate to handleRedirect - import-submit-utils.ts: Accept NavigateFunction parameter - UploadJar.tsx: Replace history.goBack with navigate(-1) - useUploadJarFormToast.ts: Accept navigate as parameter - AddServerlessFunction.tsx: Replace history.goBack with navigate(-1) - jar-file-upload-utils.ts: Accept navigate as parameter - MonitoringPage.tsx: Replace history.push with navigate - ProjectDetailsPage.tsx: Replace history.push with navigate - add-page-utils.ts: Accept navigate as parameter All utility functions updated to use dependency injection pattern. Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
783cf6a to
6819723
Compare
Replace history object usage with useNavigate hook in knative-plugin package. Changes: - EventSink.tsx: Replace history.goBack with navigate(-1) - EventSource.tsx: Replace history.goBack with navigate(-1) - AddBroker.tsx: Replace history.goBack with navigate(-1) - AddChannel.tsx: Replace history.goBack with navigate(-1) - Subscribe.tsx: Replace history.push with navigate - FunctionDetailsPage.tsx: Replace history.push with navigate - CreateKnatifyPage.tsx: Replace history.goBack with navigate(-1) - create-eventsources-utils.ts: Accept NavigateFunction parameter All components updated to use useNavigate() hook. Utility function updated to use dependency injection pattern. Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Replace history object usage with useNavigate hook in helm-plugin package. Changes: - HelmReleaseDetailsPage.tsx: Replace history.push with navigate - CreateHelmChartRepository.tsx: Replace history.goBack with navigate(-1) - CreateHelmChartRepositoryPage.tsx: Replace history.push with navigate - HelmInstallUpgradePage.tsx: Replace history.push/goBack with navigate - HelmReleaseRollbackPage.tsx: Replace history.push/goBack with navigate All components updated to use useNavigate() hook from react-router-dom-v5-compat. Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Replace history object usage with useNavigate hook in operator-lifecycle-manager package. Changes: - create-catalog-source.tsx: Replace history.goBack with navigate(-1) - operator-hub-items.tsx: Replace history.replace with navigate - operator-hub-subscribe.tsx: Replace history.push with navigate - install-plan.tsx: Replace history.push with navigate - uninstall-operator-modal.tsx: Replace history.push with navigate - operand-form.tsx: Replace history.push/replace/goBack with navigate - DEPRECATED_operand-form.tsx: Replace history.push/goBack with navigate - subscription.tsx: Replace history.push with navigate All components updated to use useNavigate() hook from react-router-dom-v5-compat. Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Replace history object usage with useNavigate hook in remaining packages. Changes: - topology/ExportViewLogButton.tsx: Replace history.push with navigate - shipwright-plugin/EditBuild.tsx: Replace history.push/goBack with navigate - metal3-plugin/AddBareMetalHost.tsx: Replace history.push with navigate - metal3-plugin/AddBareMetalHostForm.tsx: Replace history.goBack with navigate(-1) - public/QuickCreate.tsx: Replace history.push with navigate - public/attach-storage.tsx: Remove unused history type definition All components updated to use useNavigate() hook from react-router-dom-v5-compat. Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
6819723 to
ee61fdf
Compare
Replace `LocationDescriptor` and `Location` types from the history package with `To` and `Location` types from react-router-dom-v5-compat. This is part of the migration to programmatic navigation using React Router hooks instead of the history object. Changes: - Replace LocationDescriptor with To in console-types.ts (SDK) - Replace LocationDescriptor with To in delete-modal.tsx - Replace History.LocationDescriptor with To in LinkStatus.tsx - Replace Location import in telemetry.ts Breaking Change: The UseDeleteModal hook's redirectTo parameter type has changed from LocationDescriptor (history) to To (react-router-dom). Plugin developers should update their imports accordingly. Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
ee61fdf to
9eab703
Compare
| import { useMemo } from 'react'; | ||
| import { useTranslation } from 'react-i18next'; | ||
| import { useNavigate } from 'react-router-dom-v5-compat'; | ||
| import { Action } from '@console/dynamic-plugin-sdk'; | ||
| import { useDeepCompareMemoize } from '@console/dynamic-plugin-sdk/src/utils/k8s/hooks/useDeepCompareMemoize'; | ||
| import { resourceObjPath } from '@console/internal/components/utils/resource-link'; | ||
| import { JobModel } from '@console/internal/models'; | ||
| import { | ||
| k8sCreate, | ||
| CronJobKind, | ||
| JobKind, | ||
| referenceFor, | ||
| K8sResourceCommon, | ||
| } from '@console/internal/module/k8s'; | ||
| import { CronJobActionCreator } from './types'; |
There was a problem hiding this comment.
@typescript-eslint/consistent-type-imports - use import type
| }, | ||
| }; | ||
|
|
||
| return k8sCreate(JobModel, reqPayload as K8sResourceCommon); |
There was a problem hiding this comment.
type assertion doesnt appear needed
| return k8sCreate(JobModel, reqPayload as K8sResourceCommon); | |
| return k8sCreate(JobModel, reqPayload); |
| resource: 'jobs', | ||
| name: obj.metadata?.name, | ||
| namespace: obj.metadata?.namespace, | ||
| verb: 'create' as const, |
There was a problem hiding this comment.
why is as const needed? do we/should we do this elsewhere?
| const { t } = useTranslation(); | ||
| const navigate = useNavigate(); | ||
|
|
||
| const memoizedFilterActions = useDeepCompareMemoize(filterActions); | ||
|
|
||
| const factory = useMemo( | ||
| () => ({ | ||
| [CronJobActionCreator.StartJob]: () => ({ | ||
| id: 'start-job', | ||
| label: t('console-app~Start Job'), | ||
| cta: () => { |
There was a problem hiding this comment.
nit
| const { t } = useTranslation(); | |
| const navigate = useNavigate(); | |
| const memoizedFilterActions = useDeepCompareMemoize(filterActions); | |
| const factory = useMemo( | |
| () => ({ | |
| [CronJobActionCreator.StartJob]: () => ({ | |
| id: 'start-job', | |
| label: t('console-app~Start Job'), | |
| cta: () => { | |
| const { t } = useTranslation('console-app'); | |
| const navigate = useNavigate(); | |
| const memoizedFilterActions = useDeepCompareMemoize(filterActions); | |
| const factory = useMemo( | |
| () => ({ | |
| [CronJobActionCreator.StartJob]: () => ({ | |
| id: 'start-job', | |
| label: t('Start Job'), | |
| cta: () => { |
| // TODO: Show error in notification in the follow on tech-debt. | ||
| // eslint-disable-next-line no-console | ||
| console.error('Failed to start a Job.', error); | ||
| }); |
There was a problem hiding this comment.
this is already a hook. this doesn't have to be tech debt, we can just use useToast here
| requiredFileExtension.properties.handler(f, namespace); | ||
| const path = requiredFileExtension.properties.handler(f, namespace); | ||
| if (path) { | ||
| navigate(path); | ||
| } |
There was a problem hiding this comment.
does this change any existing behaviour?
| } | ||
|
|
||
| // Functional wrapper to handle location changes | ||
| const ErrorBoundary: FC<ErrorBoundaryProps> = ({ children, FallbackComponent }) => { |
There was a problem hiding this comment.
this makes locationPathname a prop from a typescript perspective when it is not
| export const jarFileUploadHandler = (file: File, namespace: string): string => { | ||
| return `/upload-jar/ns/${namespace}`; |
There was a problem hiding this comment.
i would like to DRY up this type
| export const jarFileUploadHandler = (file: File, namespace: string): string => { | |
| return `/upload-jar/ns/${namespace}`; | |
| import type { FileUploadHandler } from '@console/dynamic-plugin-sdk/src/extensions/file-upload'; | |
| export const jarFileUploadHandler: FileUploadHandler = (file, namespace) => { | |
| return `/upload-jar/ns/${namespace}`; |
| export const MONITORING_ALL_NS_PAGE_URI = '/dev-monitoring/all-namespaces'; | ||
|
|
||
| const handleNamespaceChange = (newNamespace: string): void => { | ||
| const handleNamespaceChange = (newNamespace: string, navigate: (url: string) => void): void => { |
There was a problem hiding this comment.
nit: use NavigateFunction type here for consistency
| return undefined; | ||
| } | ||
| return undefined; | ||
| } catch { | ||
| const resourceActions = knatifyResources(values, appName, true).then(() => | ||
| knatifyResources(values, appName), | ||
| ); | ||
|
|
||
| resourceActions | ||
| return resourceActions |
There was a problem hiding this comment.
why the need for all these returns?
|
@rhamilto: The following test failed, say
Full PR test history. Your PR dashboard. DetailsInstructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes-sigs/prow repository. I understand the commands that are listed here. |
|
PR needs rebase. DetailsInstructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes-sigs/prow repository. |
Summary
This PR completes the migration from the deprecated
historyobject to React Router'suseNavigatehook across all remaining packages in the OpenShift Console frontend.Changes
This PR migrates all remaining instances of the deprecated
historyobject to use theuseNavigatehook fromreact-router-dom-v5-compat, except for:public/components/app.tsx(will be handled in CONSOLE-4392)public/components/factory/modal.tsx(will be handled in CONSOLE-5012)Packages Migrated
console-app
useNavigatehookconsole-shared
useNavigatehookcatalog-utils.tsxto useNavigateFunctiontype for DRY principleslocation.pathnameas akeyprop (which caused component remounting on every URL change) to passing it as a regular prop and usingcomponentDidUpdateto reset error state only when there's an active error AND the location changes. This prevents unnecessary component remounting during tab navigation.dev-console
ProjectAccess.tsx: Replacehistory.goBackwithnavigate(-1)ProjectAccess.spec.tsx: Updated test mocks (removed obsoletehistorymock, addeduseNavigatemock)ProjectDetailsPage.tsx: AddedNavigateFunctiontype for consistencyProjectDetailsPage.spec.tsx: Updated to userenderWithProvidersutilityEditApplication.tsx,ImportForm.tsx: Removed unnecessary return statements beforehandleRedirect()import-submit-utils.ts: Changed to type-only import forNavigateFunctionknative-plugin
DeleteRevisionModalController.tsx: Replacehistory.push()withnavigate()FunctionDetailsPage.tsx: UsegeneratePath()for safer route parameter interpolationCreateKnatifyPage.tsx: Removed unnecessary return statement beforehandleRedirect()helm-plugin
useNavigatehookHelmReleaseDetailsPage.tsx: WrappedhandleNamespaceChangeinuseCallbackfor performance optimizationoperator-lifecycle-manager
create-catalog-source.tsx: Replacehistory.goBackwithnavigate(-1)operator-hub-items.tsx: Replacehistory.replacewithnavigateoperator-hub-subscribe.tsx: Replacehistory.pushwithnavigateinstall-plan.tsx: Replacehistory.pushwithnavigateuninstall-operator-modal.tsx: Replacehistory.pushwithnavigateoperand-form.tsx: Replacehistory.push/replace/goBackwith navigate equivalentsDEPRECATED_operand-form.tsx: Replacehistory.push/goBackwith navigate equivalentssubscription.tsx: Replacehistory.pushwithnavigatemetal3-plugin
AddBareMetalHost.tsx: Replacehistory.push()withnavigate()public/components
attach-storage.tsx: Removed unusedhistory: Historytype definitionconsole-dynamic-plugin-sdk
console-types.ts: Changed to type-only import forTotype from React Routerhistorypackage to React Router typesCode Quality Improvements
Based on PR review feedback:
import typesyntax where appropriate for better TypeScript practicesNavigateFunctiontype for consistencyuseCallbackwrapper for namespace change handlersgeneratePath()for route parameter interpolation to prevent potential path traversal issuesrenderWithProvidersutility for better test consistencyTest Updates
ProjectAccess.spec.tsxto remove the obsoletehistorymock and add properuseNavigatemockProjectDetailsPage.spec.tsxto userenderWithProvidersutility instead of manual MemoryRouter wrappingMigration Patterns Used
All migrations follow the established patterns:
history.push(path)→navigate(path)history.replace(path)→navigate(path, { replace: true })history.goBack()→navigate(-1)useNavigate()hook imports fromreact-router-dom-v5-compatNavigateFunctiontype for function parametersgeneratePath()for parameterized routesCommit Structure
The commits are organized by package for easier review:
CONSOLE-4990: Migrate console-app to useNavigate hookCONSOLE-4990: Migrate console-shared to useNavigate hookCONSOLE-4990: Migrate dev-console to useNavigate hookCONSOLE-4990: Migrate knative-plugin to useNavigate hookCONSOLE-4990: Migrate helm-plugin to useNavigate hookCONSOLE-4990: Migrate operator-lifecycle-manager to useNavigate hookCONSOLE-4990: Migrate remaining packages to useNavigate hookCONSOLE-4990: Replace history types with React Router typesBug Fix
Fixed resource tab navigation causing full page reloads (#15959 (comment))
The ErrorBoundary component was using
location.pathnameas akeyprop, which caused the entire component tree to remount whenever the URL changed. This resulted in:Solution: Changed to pass
locationPathnameas a prop and usecomponentDidUpdateto reset error state only when there's an active error AND the location changes. This preserves the error reset behavior while avoiding unnecessary remounts.Related Issues
Testing
useNavigate()hookCo-Authored-By: Claude Sonnet 4.5 noreply@anthropic.com