Skip to content

Commit 5c923e4

Browse files
jong-kyungnlynzaad
andauthored
fix(router): ensure useParams returns parsed params when strict is false (#6387)
* fix: optimize match retrieval by using a Map for previous matches * feat: add strict false parameter handling and related routes * replace params only when routeParams is fully parsed --------- Co-authored-by: Nico Lynzaad <nlynzaad@zylangroup.com> Co-authored-by: Nico Lynzaad <44094871+nlynzaad@users.noreply.github.com>
1 parent be8849e commit 5c923e4

File tree

7 files changed

+251
-10
lines changed

7 files changed

+251
-10
lines changed

e2e/react-router/basic-file-based/src/routeTree.gen.ts

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ import { Route as anotherGroupOnlyrouteinsideRouteImport } from './routes/(anoth
4242
import { Route as RelativeUseNavigateRouteRouteImport } from './routes/relative/useNavigate/route'
4343
import { Route as RelativeLinkRouteRouteImport } from './routes/relative/link/route'
4444
import { Route as PathlessLayoutLayoutRouteRouteImport } from './routes/pathless-layout/_layout/route'
45+
import { Route as ParamsPsStrictFalseRouteRouteImport } from './routes/params-ps/strict-false/route'
4546
import { Route as ParamsPsNonNestedRouteRouteImport } from './routes/params-ps/non-nested/route'
4647
import { Route as NonNestedSuffixRouteRouteImport } from './routes/non-nested/suffix/route'
4748
import { Route as NonNestedPrefixRouteRouteImport } from './routes/non-nested/prefix/route'
@@ -87,6 +88,7 @@ import { Route as LayoutLayout2LayoutBRouteImport } from './routes/_layout/_layo
8788
import { Route as LayoutLayout2LayoutARouteImport } from './routes/_layout/_layout-2/layout-a'
8889
import { Route as groupSubfolderInsideRouteImport } from './routes/(group)/subfolder/inside'
8990
import { Route as groupLayoutInsidelayoutRouteImport } from './routes/(group)/_layout.insidelayout'
91+
import { Route as ParamsPsStrictFalseVersionRouteRouteImport } from './routes/params-ps/strict-false/$version.route'
9092
import { Route as ParamsPsNonNestedFooRouteRouteImport } from './routes/params-ps/non-nested/$foo_/route'
9193
import { Route as ParamsPsNamedFooRouteRouteImport } from './routes/params-ps/named/$foo/route'
9294
import { Route as NonNestedSuffixChar123bazChar125suffixRouteRouteImport } from './routes/non-nested/suffix/{$baz}suffix.route'
@@ -295,6 +297,12 @@ const PathlessLayoutLayoutRouteRoute =
295297
id: '/_layout',
296298
getParentRoute: () => PathlessLayoutRouteRoute,
297299
} as any)
300+
const ParamsPsStrictFalseRouteRoute =
301+
ParamsPsStrictFalseRouteRouteImport.update({
302+
id: '/params-ps/strict-false',
303+
path: '/params-ps/strict-false',
304+
getParentRoute: () => rootRouteImport,
305+
} as any)
298306
const ParamsPsNonNestedRouteRoute = ParamsPsNonNestedRouteRouteImport.update({
299307
id: '/params-ps/non-nested',
300308
path: '/params-ps/non-nested',
@@ -539,6 +547,12 @@ const groupLayoutInsidelayoutRoute = groupLayoutInsidelayoutRouteImport.update({
539547
path: '/insidelayout',
540548
getParentRoute: () => groupLayoutRoute,
541549
} as any)
550+
const ParamsPsStrictFalseVersionRouteRoute =
551+
ParamsPsStrictFalseVersionRouteRouteImport.update({
552+
id: '/$version',
553+
path: '/$version',
554+
getParentRoute: () => ParamsPsStrictFalseRouteRoute,
555+
} as any)
542556
const ParamsPsNonNestedFooRouteRoute =
543557
ParamsPsNonNestedFooRouteRouteImport.update({
544558
id: '/$foo_',
@@ -780,6 +794,7 @@ export interface FileRoutesByFullPath {
780794
'/non-nested/prefix': typeof NonNestedPrefixRouteRouteWithChildren
781795
'/non-nested/suffix': typeof NonNestedSuffixRouteRouteWithChildren
782796
'/params-ps/non-nested': typeof ParamsPsNonNestedRouteRouteWithChildren
797+
'/params-ps/strict-false': typeof ParamsPsStrictFalseRouteRouteWithChildren
783798
'/relative/link': typeof RelativeLinkRouteRouteWithChildren
784799
'/relative/useNavigate': typeof RelativeUseNavigateRouteRouteWithChildren
785800
'/onlyrouteinside': typeof anotherGroupOnlyrouteinsideRoute
@@ -801,6 +816,7 @@ export interface FileRoutesByFullPath {
801816
'/non-nested/suffix/{$baz}suffix': typeof NonNestedSuffixChar123bazChar125suffixRouteRouteWithChildren
802817
'/params-ps/named/$foo': typeof ParamsPsNamedFooRouteRouteWithChildren
803818
'/params-ps/non-nested/$foo': typeof ParamsPsNonNestedFooRouteRouteWithChildren
819+
'/params-ps/strict-false/$version': typeof ParamsPsStrictFalseVersionRouteRoute
804820
'/insidelayout': typeof groupLayoutInsidelayoutRoute
805821
'/subfolder/inside': typeof groupSubfolderInsideRoute
806822
'/layout-a': typeof LayoutLayout2LayoutARoute
@@ -892,6 +908,7 @@ export interface FileRoutesByTo {
892908
'/non-nested/prefix': typeof NonNestedPrefixRouteRouteWithChildren
893909
'/non-nested/suffix': typeof NonNestedSuffixRouteRouteWithChildren
894910
'/params-ps/non-nested': typeof ParamsPsNonNestedRouteRouteWithChildren
911+
'/params-ps/strict-false': typeof ParamsPsStrictFalseRouteRouteWithChildren
895912
'/relative/link': typeof RelativeLinkRouteRouteWithChildren
896913
'/relative/useNavigate': typeof RelativeUseNavigateRouteRouteWithChildren
897914
'/onlyrouteinside': typeof anotherGroupOnlyrouteinsideRoute
@@ -907,6 +924,7 @@ export interface FileRoutesByTo {
907924
'/search-params': typeof SearchParamsIndexRoute
908925
'/params-ps/named/$foo': typeof ParamsPsNamedFooRouteRouteWithChildren
909926
'/params-ps/non-nested/$foo': typeof ParamsPsNonNestedFooRouteRouteWithChildren
927+
'/params-ps/strict-false/$version': typeof ParamsPsStrictFalseVersionRouteRoute
910928
'/insidelayout': typeof groupLayoutInsidelayoutRoute
911929
'/subfolder/inside': typeof groupSubfolderInsideRoute
912930
'/layout-a': typeof LayoutLayout2LayoutARoute
@@ -999,6 +1017,7 @@ export interface FileRoutesById {
9991017
'/non-nested/prefix': typeof NonNestedPrefixRouteRouteWithChildren
10001018
'/non-nested/suffix': typeof NonNestedSuffixRouteRouteWithChildren
10011019
'/params-ps/non-nested': typeof ParamsPsNonNestedRouteRouteWithChildren
1020+
'/params-ps/strict-false': typeof ParamsPsStrictFalseRouteRouteWithChildren
10021021
'/pathless-layout/_layout': typeof PathlessLayoutLayoutRouteRouteWithChildren
10031022
'/relative/link': typeof RelativeLinkRouteRouteWithChildren
10041023
'/relative/useNavigate': typeof RelativeUseNavigateRouteRouteWithChildren
@@ -1023,6 +1042,7 @@ export interface FileRoutesById {
10231042
'/non-nested/suffix/{$baz}suffix': typeof NonNestedSuffixChar123bazChar125suffixRouteRouteWithChildren
10241043
'/params-ps/named/$foo': typeof ParamsPsNamedFooRouteRouteWithChildren
10251044
'/params-ps/non-nested/$foo_': typeof ParamsPsNonNestedFooRouteRouteWithChildren
1045+
'/params-ps/strict-false/$version': typeof ParamsPsStrictFalseVersionRouteRoute
10261046
'/(group)/_layout/insidelayout': typeof groupLayoutInsidelayoutRoute
10271047
'/(group)/subfolder/inside': typeof groupSubfolderInsideRoute
10281048
'/_layout/_layout-2/layout-a': typeof LayoutLayout2LayoutARoute
@@ -1118,6 +1138,7 @@ export interface FileRouteTypes {
11181138
| '/non-nested/prefix'
11191139
| '/non-nested/suffix'
11201140
| '/params-ps/non-nested'
1141+
| '/params-ps/strict-false'
11211142
| '/relative/link'
11221143
| '/relative/useNavigate'
11231144
| '/onlyrouteinside'
@@ -1139,6 +1160,7 @@ export interface FileRouteTypes {
11391160
| '/non-nested/suffix/{$baz}suffix'
11401161
| '/params-ps/named/$foo'
11411162
| '/params-ps/non-nested/$foo'
1163+
| '/params-ps/strict-false/$version'
11421164
| '/insidelayout'
11431165
| '/subfolder/inside'
11441166
| '/layout-a'
@@ -1230,6 +1252,7 @@ export interface FileRouteTypes {
12301252
| '/non-nested/prefix'
12311253
| '/non-nested/suffix'
12321254
| '/params-ps/non-nested'
1255+
| '/params-ps/strict-false'
12331256
| '/relative/link'
12341257
| '/relative/useNavigate'
12351258
| '/onlyrouteinside'
@@ -1245,6 +1268,7 @@ export interface FileRouteTypes {
12451268
| '/search-params'
12461269
| '/params-ps/named/$foo'
12471270
| '/params-ps/non-nested/$foo'
1271+
| '/params-ps/strict-false/$version'
12481272
| '/insidelayout'
12491273
| '/subfolder/inside'
12501274
| '/layout-a'
@@ -1336,6 +1360,7 @@ export interface FileRouteTypes {
13361360
| '/non-nested/prefix'
13371361
| '/non-nested/suffix'
13381362
| '/params-ps/non-nested'
1363+
| '/params-ps/strict-false'
13391364
| '/pathless-layout/_layout'
13401365
| '/relative/link'
13411366
| '/relative/useNavigate'
@@ -1360,6 +1385,7 @@ export interface FileRouteTypes {
13601385
| '/non-nested/suffix/{$baz}suffix'
13611386
| '/params-ps/named/$foo'
13621387
| '/params-ps/non-nested/$foo_'
1388+
| '/params-ps/strict-false/$version'
13631389
| '/(group)/_layout/insidelayout'
13641390
| '/(group)/subfolder/inside'
13651391
| '/_layout/_layout-2/layout-a'
@@ -1450,6 +1476,7 @@ export interface RootRouteChildren {
14501476
PostsRoute: typeof PostsRouteWithChildren
14511477
RemountDepsRoute: typeof RemountDepsRoute
14521478
ParamsPsNonNestedRouteRoute: typeof ParamsPsNonNestedRouteRouteWithChildren
1479+
ParamsPsStrictFalseRouteRoute: typeof ParamsPsStrictFalseRouteRouteWithChildren
14531480
RelativeLinkRouteRoute: typeof RelativeLinkRouteRouteWithChildren
14541481
RelativeUseNavigateRouteRoute: typeof RelativeUseNavigateRouteRouteWithChildren
14551482
anotherGroupOnlyrouteinsideRoute: typeof anotherGroupOnlyrouteinsideRoute
@@ -1712,6 +1739,13 @@ declare module '@tanstack/react-router' {
17121739
preLoaderRoute: typeof PathlessLayoutLayoutRouteRouteImport
17131740
parentRoute: typeof PathlessLayoutRouteRoute
17141741
}
1742+
'/params-ps/strict-false': {
1743+
id: '/params-ps/strict-false'
1744+
path: '/params-ps/strict-false'
1745+
fullPath: '/params-ps/strict-false'
1746+
preLoaderRoute: typeof ParamsPsStrictFalseRouteRouteImport
1747+
parentRoute: typeof rootRouteImport
1748+
}
17151749
'/params-ps/non-nested': {
17161750
id: '/params-ps/non-nested'
17171751
path: '/params-ps/non-nested'
@@ -2027,6 +2061,13 @@ declare module '@tanstack/react-router' {
20272061
preLoaderRoute: typeof groupLayoutInsidelayoutRouteImport
20282062
parentRoute: typeof groupLayoutRoute
20292063
}
2064+
'/params-ps/strict-false/$version': {
2065+
id: '/params-ps/strict-false/$version'
2066+
path: '/$version'
2067+
fullPath: '/params-ps/strict-false/$version'
2068+
preLoaderRoute: typeof ParamsPsStrictFalseVersionRouteRouteImport
2069+
parentRoute: typeof ParamsPsStrictFalseRouteRoute
2070+
}
20302071
'/params-ps/non-nested/$foo_': {
20312072
id: '/params-ps/non-nested/$foo_'
20322073
path: '/$foo'
@@ -2669,6 +2710,20 @@ const ParamsPsNonNestedRouteRouteWithChildren =
26692710
ParamsPsNonNestedRouteRouteChildren,
26702711
)
26712712

2713+
interface ParamsPsStrictFalseRouteRouteChildren {
2714+
ParamsPsStrictFalseVersionRouteRoute: typeof ParamsPsStrictFalseVersionRouteRoute
2715+
}
2716+
2717+
const ParamsPsStrictFalseRouteRouteChildren: ParamsPsStrictFalseRouteRouteChildren =
2718+
{
2719+
ParamsPsStrictFalseVersionRouteRoute: ParamsPsStrictFalseVersionRouteRoute,
2720+
}
2721+
2722+
const ParamsPsStrictFalseRouteRouteWithChildren =
2723+
ParamsPsStrictFalseRouteRoute._addFileChildren(
2724+
ParamsPsStrictFalseRouteRouteChildren,
2725+
)
2726+
26722727
interface RelativeLinkRouteRouteChildren {
26732728
RelativeLinkRelativeLinkARoute: typeof RelativeLinkRelativeLinkARoute
26742729
RelativeLinkRelativeLinkBRoute: typeof RelativeLinkRelativeLinkBRoute
@@ -2811,6 +2866,7 @@ const rootRouteChildren: RootRouteChildren = {
28112866
PostsRoute: PostsRouteWithChildren,
28122867
RemountDepsRoute: RemountDepsRoute,
28132868
ParamsPsNonNestedRouteRoute: ParamsPsNonNestedRouteRouteWithChildren,
2869+
ParamsPsStrictFalseRouteRoute: ParamsPsStrictFalseRouteRouteWithChildren,
28142870
RelativeLinkRouteRoute: RelativeLinkRouteRouteWithChildren,
28152871
RelativeUseNavigateRouteRoute: RelativeUseNavigateRouteRouteWithChildren,
28162872
anotherGroupOnlyrouteinsideRoute: anotherGroupOnlyrouteinsideRoute,

e2e/react-router/basic-file-based/src/routes/params-ps/index.tsx

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,28 @@ function RouteComponent() {
122122
</Link>
123123
</li>
124124
</ul>
125+
<hr />
126+
<h3 className="pb-2">Parsed params with strict false</h3>
127+
<ul className="grid mb-2">
128+
<li>
129+
<Link
130+
data-testid="strict-false-version-1"
131+
to="/params-ps/strict-false/$version"
132+
params={{ version: 1 }}
133+
>
134+
/params-ps/strict-false/$version (1)
135+
</Link>
136+
</li>
137+
<li>
138+
<Link
139+
data-testid="strict-false-version-2"
140+
to="/params-ps/strict-false/$version"
141+
params={{ version: 2 }}
142+
>
143+
/params-ps/strict-false/$version (2)
144+
</Link>
145+
</li>
146+
</ul>
125147
</div>
126148
)
127149
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import { createFileRoute, useParams } from '@tanstack/react-router'
2+
3+
export const Route = createFileRoute('/params-ps/strict-false/$version')({
4+
params: {
5+
parse: (params) => ({
6+
...params,
7+
version: parseInt(params.version),
8+
}),
9+
stringify: (params) => ({
10+
...params,
11+
version: `${params.version}`,
12+
}),
13+
},
14+
component: RouteComponent,
15+
})
16+
17+
function RouteComponent() {
18+
const { version } = useParams({ strict: false })
19+
20+
return <div data-testid="strict-false-child-version">{String(version)}</div>
21+
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
import {
2+
Link,
3+
Outlet,
4+
createFileRoute,
5+
useParams,
6+
} from '@tanstack/react-router'
7+
8+
export const Route = createFileRoute('/params-ps/strict-false')({
9+
component: RouteComponent,
10+
})
11+
12+
function RouteComponent() {
13+
const { version } = useParams({ strict: false })
14+
return (
15+
<div>
16+
<h3>ParamsStrictFalseParse</h3>
17+
<div>
18+
Type:{' '}
19+
<span data-testid="strict-false-version-type">{typeof version}</span>
20+
</div>
21+
<div>
22+
Value:{' '}
23+
<span data-testid="strict-false-version-value">{String(version)}</span>
24+
</div>
25+
<Link
26+
data-testid="strict-false-version-1"
27+
from={Route.fullPath}
28+
to="./$version"
29+
params={{ version: 1 }}
30+
>
31+
Version 1
32+
</Link>
33+
<Link
34+
data-testid="strict-false-version-2"
35+
from={Route.fullPath}
36+
to="./$version"
37+
params={{ version: 2 }}
38+
>
39+
Version 2
40+
</Link>
41+
<Outlet />
42+
</div>
43+
)
44+
}

e2e/react-router/basic-file-based/tests/params.spec.ts

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -492,3 +492,29 @@ test.describe('Unicode params', () => {
492492
})
493493
})
494494
})
495+
496+
test.describe('useParams strict false uses parsed child params', () => {
497+
test.beforeEach(async ({ page }) => {
498+
await page.goto('/params-ps')
499+
})
500+
501+
test('parent receives parsed values after child navigation', async ({
502+
page,
503+
}) => {
504+
await page.getByTestId('strict-false-version-1').click()
505+
await page.waitForURL('/params-ps/strict-false/1')
506+
507+
await expect(page.getByTestId('strict-false-version-type')).toHaveText(
508+
'number',
509+
)
510+
await expect(page.getByTestId('strict-false-version-value')).toHaveText('1')
511+
512+
await page.getByTestId('strict-false-version-2').click()
513+
await page.waitForURL('/params-ps/strict-false/2')
514+
515+
await expect(page.getByTestId('strict-false-version-type')).toHaveText(
516+
'number',
517+
)
518+
await expect(page.getByTestId('strict-false-version-value')).toHaveText('2')
519+
})
520+
})

0 commit comments

Comments
 (0)