feat: useRouter().pathname → usePathname() (#928)#938
Merged
Conversation
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 tasks
Contributor
There was a problem hiding this comment.
Pull request overview
This PR advances the Next.js 16 upgrade plan by replacing useRouter().pathname usages (from next/router) with usePathname() (from next/navigation) at two call sites, improving pathname matching behavior on dynamic routes while keeping one known-sensitive pathname usage intentionally deferred.
Changes:
- Updated
ExportMethodViewto derive the current path viausePathname()and removed theuseRouterdependency. - Updated
useStateSyncto useusePathname()so popstate/back-forward detection can compare against the resolved URL (fixing dynamic-route mismatch). - Added an inline deferral note in
useUpdateURLCatalogParamsexplaining whyuseRouter().pathnameremains for now (to preserve dynamic route interpolation behavior), tracked for follow-up work.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated no comments.
| File | Description |
|---|---|
| src/views/ExportMethodView/exportMethodView.tsx | Migrates pathname read to usePathname() and removes useRouter import. |
| src/hooks/useUpdateURLCatalogParam.ts | Adds rationale/comment documenting intentional deferral of pathname migration in this hook. |
| src/hooks/stateSyncManager/hooks/UseStateSync/hook.ts | Migrates pathname read to usePathname() to correctly match resolved history URLs in popstate handling. |
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Contributor
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 4 out of 4 changed files in this pull request and generated 2 comments.
Comments suppressed due to low confidence (1)
src/hooks/stateSyncManager/hooks/UseStateSync/hook.ts:37
useStateSyncnow usesusePathname()(resolved URL) butwasPop()compares againstNextHistoryState.urland prependsbasePath. In Next.js history state,asis the resolved URL (see src/hooks/stateSyncManager/hooks/UseBeforePopState/hook.ts wherestate.asis treated as the navigated URL). This mismatch can causewasPop()to return false on back/forward navigations (especially on dynamic routes), leading to incorrect state/URL sync behavior. Consider updatingwasPop()to compare againstnextHistoryState.as(stripping query) and revisiting basePath handling (avoid double-prepending ifusePathname()already includes basePath).
const { basePath, isReady, query } = useRouter();
const pathname = usePathname() ?? "";
const { onClearPopRef, popRef } = useWasPop();
// Extract the query from the state.
const stateQuery = state.query as NextRouter["query"];
// Extract the param keys from the state.
const paramKeys = state.paramKeys;
// Dispatch action to sync state <-> URL.
useEffect(() => {
// Do nothing if the router is not ready.
if (!isReady) return;
// Do nothing; URL and state are in sync.
if (isSynced(stateQuery, query)) {
onClearPopRef();
return;
}
// Dispatch action sync URL >> state.
if (wasPop(basePath, pathname, popRef.current)) {
// When the user navigates with the back/forward buttons.
…#928) NextHistoryState.url is the href stored by Next.js — the route pattern on dynamic routes — not the resolved URL, so the previous code was correctly using useRouter().pathname (also a route pattern). Migrating that site to usePathname() (resolved URL) would have broken wasPop() on dynamic routes. Reverts the useStateSync portion of the migration. Updates the unit tests to reflect the actual NextHistoryState contract. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
NoopDog
approved these changes
May 26, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Part of #884 (Next.js 16 upgrade plan).
Migrates one safe call site from
useRouter().pathname(next/router) tousePathname()(next/navigation):src/views/ExportMethodView/exportMethodView.tsx—pathnameis compared against staticexportMethods[].routestrings. These are non-dynamic routes, so resolved URL == route pattern; the migration is behaviour-preserving. Removed the now-unuseduseRouterimport.Uses
usePathname() ?? ""to handle thestring | nullreturn type, mirroring the existing pattern inHeader.tsx.Deferrals
Two sites are left on
useRouter().pathnamefor the same underlying reason —pathnameneeds to be in the route-pattern form (with bracketed dynamic segments) because the surrounding code interacts with other Next.js APIs that also use the route-pattern form. Migrating onlypathnamewould mix forms and break behaviour.src/hooks/useUpdateURLCatalogParam.ts— feedspathnameintoRouter.replace({ pathname, query }), which on dynamic routes interpolates dynamic-param keys (entityListType,entityId, …) out ofqueryinto the path. Resolved URLs would leak those keys into the URL query string. Migration is coupled with replacingRouter.replaceitself; tracked in [P1.3] Next.js 16 prep: useRouter().push/.replace → next/navigation useRouter() #930 and [P1.4] Next.js 16 prep: useRouter().query → useSearchParams() (non-dynamic-param sites) #931. Inline comment added in this PR.src/hooks/stateSyncManager/hooks/UseStateSync/hook.ts—pathnameis compared againstnextHistoryState.urlinsidewasPop().nextHistoryState.urlis the href Next.js stores in history (route pattern on dynamic routes, not the resolved URL). Both sides must remain in the route-pattern form for the comparison to work. Migration here will pair with awasPoprefactor to compare againststate.as(the resolved URL); deferring to the same broader migration tracked in [P1.3] Next.js 16 prep: useRouter().push/.replace → next/navigation useRouter() #930. (Thanks to Copilot for catching the regression in the first revision of this PR.)Tests
New unit tests in
tests/stateSyncManager_utils.test.tscover all four exports ofUseStateSync/utils.ts(wasPop,hasParams,isSynced,stringifyQuery) — 20 cases, including ones that pin thewasPopcontract: both sides must be in the same form (route pattern or resolved URL).Test plan
npm run lint— cleannpm run check-format— cleannpm run test-compile— cleannpm test— 447/447 pass (new file: 20 tests)fran/x-nextjs-test— filter pages and dynamic detail pages behave the same as before.Closes #928