-
-
Notifications
You must be signed in to change notification settings - Fork 1.3k
fix(router-core): params.parse should run as minimally as possible #5124
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
WalkthroughReworks router-core match construction to reuse existing match _strictParams and only run params.parse when necessary, attaching any parse error to the match. Updates React and Solid tests to use string Changes
Sequence Diagram(s)sequenceDiagram
autonumber
actor User
participant Router
participant MatchStore as Match Store
participant Parser as Params Parser
participant Loader
User->>Router: navigate(path, opts)
Router->>Router: interpolatePath(path) -> usedParams
Router->>Router: compute loaderDepsHash
Router->>MatchStore: get(matchId = interpolatedPath + depsHash)
alt existing match found
MatchStore-->>Router: existingMatch
Router->>Router: strictParams = existingMatch._strictParams
else no existing match
Router->>Parser: run route.params.parse(strictParams) if configured
alt parse success
Parser-->>Router: strictParams
else parse failure
Parser-->>Router: throw or return paramsError
Router->>Router: attach paramsError to match (or throw if opts.throwOnError)
end
end
Router->>Router: routeParams = {...routeParams, ...strictParams}
Router->>Loader: invoke loader(routeParams) as needed
Loader-->>Router: data
Router->>MatchStore: store match {_strictParams, paramsError, data}
Router-->>User: update state / render
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Suggested reviewers
Pre-merge checks (4 passed, 1 warning)❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
Poem
Tip 👮 Agentic pre-merge checks are now available in preview!Pro plan users can now enable pre-merge checks in their settings to enforce checklists before merging PRs.
Please see the documentation for more information. Example: reviews:
pre_merge_checks:
custom_checks:
- name: "Undocumented Breaking Changes"
mode: "warning"
instructions: |
Pass/fail criteria: All breaking changes to public APIs, CLI flags, environment variables, configuration keys, database schemas, or HTTP/GraphQL endpoints must be documented in the "Breaking Change" section of the PR description and in CHANGELOG.md. Exclude purely internal or private changes (e.g., code not exported from package entry points or explicitly marked as internal). Please share your feedback with us on this Discord post. ✨ Finishing touches
🧪 Generate unit tests
Comment |
View your CI Pipeline Execution ↗ for commit 4858951
☁️ Nx Cloud last updated this comment at |
More templates
@tanstack/arktype-adapter
@tanstack/directive-functions-plugin
@tanstack/eslint-plugin-router
@tanstack/history
@tanstack/react-router
@tanstack/react-router-devtools
@tanstack/react-router-ssr-query
@tanstack/react-start
@tanstack/react-start-client
@tanstack/react-start-plugin
@tanstack/react-start-server
@tanstack/router-cli
@tanstack/router-core
@tanstack/router-devtools
@tanstack/router-devtools-core
@tanstack/router-generator
@tanstack/router-plugin
@tanstack/router-ssr-query-core
@tanstack/router-utils
@tanstack/router-vite-plugin
@tanstack/server-functions-plugin
@tanstack/solid-router
@tanstack/solid-router-devtools
@tanstack/solid-start
@tanstack/solid-start-client
@tanstack/solid-start-plugin
@tanstack/solid-start-server
@tanstack/start-client-core
@tanstack/start-plugin-core
@tanstack/start-server-core
@tanstack/start-server-functions-client
@tanstack/start-server-functions-fetcher
@tanstack/start-server-functions-server
@tanstack/start-storage-context
@tanstack/valibot-adapter
@tanstack/virtual-file-routes
@tanstack/zod-adapter
commit: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 2
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (3)
packages/react-router/tests/useParams.test.tsx
(5 hunks)packages/router-core/src/router.ts
(2 hunks)packages/solid-router/tests/useParams.test.tsx
(5 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
- GitHub Check: Test
- GitHub Check: Preview
🔇 Additional comments (5)
packages/router-core/src/router.ts (3)
1200-1209
: LGTM! Optimization correctly reuses existing match dataThe implementation correctly prevents redundant parsing by checking for an existing match and reusing its
_strictParams
. This directly addresses the issue where params.parse was being called unnecessarily.
1214-1239
: Robust implementation of conditional parameter parsingThe logic correctly:
- Reuses
_strictParams
from existing matches when available- Falls back to
interpolatePathResult.usedParams
for new matches- Only invokes
params.parse
when there's no existing match and parsing is configured- Properly handles errors with the new
paramsError
variableThis ensures params.parse runs minimally as intended.
1241-1241
: Good use of Object.assign for parameter mergingUsing
Object.assign(routeParams, strictParams)
efficiently merges the strict parameters into the route parameters object.packages/react-router/tests/useParams.test.tsx (2)
73-85
: Test correctly validates params.parse minimizationThe test setup appropriately:
- Adds console.log() to track parse invocations
- Converts the parse function to work with string params (postId as '1' or '2')
- Updates the loader to use
parseInt(params.postId)
for type conversionThis validates that the parse function receives the correct string type as expected per issue #5123.
186-250
: Comprehensive test assertions for parse invocation trackingThe test thoroughly validates the optimization by:
- Clearing and checking console spy at each navigation step
- Asserting specific call counts to ensure parse runs minimally
- Verifying the correct param values are displayed
Excellent test coverage for the PR objectives.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
♻️ Duplicate comments (2)
packages/solid-router/tests/useParams.test.tsx (1)
27-27
: Resolved: replaced no-op console.log with a vi.fn spy.Good switch to a proper spy for deterministic assertions.
packages/react-router/tests/useParams.test.tsx (1)
27-27
: Resolved: replaced no-op console.log with a vi.fn spy.Nice cleanup; mirrors the Solid test approach.
🧹 Nitpick comments (9)
packages/solid-router/tests/useParams.test.tsx (4)
75-82
: Avoid defaulting to '2' in parse; use an explicit map or pass-through.Defaulting any non-'one' value to '2' can mask issues and inflate parse counts. Map known values and otherwise pass through.
- parse: (params) => { - mockedfn() - return { - ...params, - postId: params.postId === 'one' ? '1' : '2', - } - }, + parse: (params) => { + mockedfn() + const idMap = { one: '1', two: '2' } as const + return { ...params, postId: idMap[params.postId] ?? params.postId } + },
85-85
: Specify radix for parseInt.Be explicit to avoid edge cases with leading zeros.
- post: posts.find((post) => post.id === parseInt(params.postId)), + post: posts.find((post) => post.id === parseInt(params.postId, 10)),
191-191
: Parse call-count assertions are brittle.Exact-call assertions may fail with benign internal changes. Prefer asserting upper bounds and/or validating parse inputs.
Example adjustments:
- Upper bound:
expect(mockedfn.mock.calls.length).toBeLessThanOrEqual(N)
- Validate inputs (Solid): capture params passed into
parse
with a vi.fn wrapper and assertpostId
is 'one'/'two' on respective navigations.Also applies to: 214-214, 225-225, 247-247
184-184
: Reduce repetition for mock resets.Extract a helper or reset in a scoped utility to keep the test flow concise.
- mockedfn.mockClear() + // helper + const resetParseSpy = () => mockedfn.mockClear() + resetParseSpy()Also applies to: 193-193, 217-217, 227-227
packages/react-router/tests/useParams.test.tsx (5)
74-77
: Specify radix for parseInt in loader.- loader: ({ params }) => { - return { post: posts.find((post) => post.id === parseInt(params.postId)) } - }, + loader: ({ params }) => { + return { post: posts.find((post) => post.id === parseInt(params.postId, 10)) } + },
78-85
: Avoid defaulting to '2' in parse; use an explicit map or pass-through.- params: { - parse: (params) => { - mockedfn() - return { - ...params, - postId: params.postId === 'one' ? '1' : '2', - } - }, - }, + params: { + parse: (params) => { + mockedfn() + const idMap = { one: '1', two: '2' } as const + return { ...params, postId: idMap[params.postId] ?? params.postId } + }, + },
177-177
: Use asynchronousact
correctly with promises.Wrap the awaited call in an async
act
block to avoid warnings.- await act(() => router.load()) + await act(async () => { + await router.load() + })
186-186
:act
around sync clicks doesn’t needawait
; make it consistently sync.Keeps intent clear and avoids implying asynchrony where there is none.
- await act(() => fireEvent.click(firstCategoryLink)) + act(() => { + fireEvent.click(firstCategoryLink) + })Apply similarly to the other click handlers in this file.
Also applies to: 195-195, 220-220, 230-230
192-192
: Parse call-count assertions may be too strict.Assert upper bounds and/or validate the params received by parse to reduce brittleness while still guarding “minimal” execution.
Example:
expect(mockedfn.mock.calls.length).toBeLessThanOrEqual(N) // or // const parsePost = vi.fn((p) => ({...})) // expect(parsePost).toHaveBeenLastCalledWith(expect.objectContaining({ postId: 'one' }))Also applies to: 216-216, 227-227, 249-249
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
packages/react-router/tests/useParams.test.tsx
(5 hunks)packages/solid-router/tests/useParams.test.tsx
(5 hunks)
🔇 Additional comments (2)
packages/solid-router/tests/useParams.test.tsx (1)
123-125
: LGTM: relativeto="./$postId"
with params.This properly defers encoding to the router and avoids manual string interpolation.
packages/react-router/tests/useParams.test.tsx (1)
124-126
: LGTM:to="./$postId"
with params.Removes brittle string interpolation and leverages route semantics.
…anStack#5124) resolves TanStack#5123 and adds checks to tests to ensure this runs as minimally as possible <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Performance** * Reuses existing route matches to reduce redundant param parsing and stabilize navigation/loading behavior. * **Tests** * Updated tests to match the new postId param shape, navigation paths, and parameter displays; added mocks to verify param parsing calls. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
resolves #5123 and adds checks to tests to ensure this runs as minimally as possible
Summary by CodeRabbit