Skip to content

Commit 3b856e6

Browse files
authored
fix: pass in full pathname + search in TSR to avoid trailing slashes (#1217)
1 parent 7f9411a commit 3b856e6

File tree

3 files changed

+71
-16
lines changed

3 files changed

+71
-16
lines changed
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
it('does not append a trailing slash', () => {
2+
cy.visit('/trailing-slash')
3+
cy.location('pathname').should('eq', '/trailing-slash')
4+
cy.contains('Set declared').click()
5+
cy.location('pathname').should('eq', '/trailing-slash')
6+
cy.contains('Clear').click()
7+
cy.location('pathname').should('eq', '/trailing-slash')
8+
cy.contains('Set undeclared').click()
9+
cy.location('pathname').should('eq', '/trailing-slash')
10+
cy.contains('Clear').click()
11+
cy.location('pathname').should('eq', '/trailing-slash')
12+
})
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
import { createFileRoute } from '@tanstack/react-router'
2+
import { createStandardSchemaV1, parseAsString, useQueryStates } from 'nuqs'
3+
4+
const declared = {
5+
declared: parseAsString
6+
}
7+
const undeclared = {
8+
undeclared: parseAsString
9+
}
10+
11+
const searchParams = {
12+
...declared,
13+
...undeclared
14+
}
15+
16+
export const Route = createFileRoute('/trailing-slash')({
17+
validateSearch: createStandardSchemaV1(declared, {
18+
partialOutput: true
19+
}),
20+
component: TrailingSlashTest
21+
})
22+
23+
function TrailingSlashTest() {
24+
const [{ declared, undeclared }, setSearchParams] =
25+
useQueryStates(searchParams)
26+
return (
27+
<div>
28+
<button
29+
onClick={() => {
30+
setSearchParams({
31+
declared: 'pass'
32+
})
33+
}}
34+
>
35+
Set declared
36+
</button>
37+
<button
38+
onClick={() => {
39+
setSearchParams({
40+
undeclared: 'pass'
41+
})
42+
}}
43+
>
44+
Set undeclared
45+
</button>
46+
<button onClick={() => setSearchParams(null)}>Clear</button>
47+
<pre>{JSON.stringify({ declared, undeclared }, null, 2)}</pre>
48+
</div>
49+
)
50+
}

packages/nuqs/src/adapters/tanstack-router.ts

Lines changed: 9 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,18 @@
1-
import { useLocation, useMatches, useNavigate } from '@tanstack/react-router'
1+
import { useLocation, useNavigate } from '@tanstack/react-router'
22
import { startTransition, useCallback, useMemo } from 'react'
33
import { renderQueryString } from '../lib/url-encoding'
44
import { createAdapterProvider, type AdapterProvider } from './lib/context'
55
import type { AdapterInterface, UpdateUrlFunction } from './lib/defs'
66

77
function useNuqsTanstackRouterAdapter(watchKeys: string[]): AdapterInterface {
8+
const pathname = useLocation({ select: state => state.pathname })
89
const search = useLocation({
910
select: state =>
1011
Object.fromEntries(
1112
Object.entries(state.search).filter(([key]) => watchKeys.includes(key))
1213
)
1314
})
1415
const navigate = useNavigate()
15-
const from = useMatches({
16-
select: matches =>
17-
matches.length > 0
18-
? (matches[matches.length - 1]?.fullPath as string)
19-
: undefined
20-
})
2116
const searchParams = useMemo(
2217
() =>
2318
// search is a Record<string, string | number | object | Array<string | number>>,
@@ -56,20 +51,18 @@ function useNuqsTanstackRouterAdapter(watchKeys: string[]): AdapterInterface {
5651
// TBC if it causes issues with consuming those search params
5752
// in other parts of the app.
5853
//
59-
// When we clear the search, passing an empty string causes
60-
// a type error and possible basepath issues, so we switch it to '.' instead.
61-
// See https://github.com/47ng/nuqs/pull/953#issuecomment-3003583471
62-
to: renderQueryString(search) || '.',
63-
// `from` will be handled by tanstack router match resolver, code snippet:
64-
// https://github.com/TanStack/router/blob/5d940e2d8bdb12e213eede0abe8012855433ec4b/packages/react-router/src/link.tsx#L108-L112
65-
...(from ? { from } : {}),
54+
// Note: we need to specify pathname + search here to avoid TSR appending
55+
// a trailing slash to the pathname, see https://github.com/47ng/nuqs/issues/1215
56+
from: '/',
57+
to: pathname + renderQueryString(search),
6658
replace: options.history === 'replace',
6759
resetScroll: options.scroll,
68-
hash: prevHash => prevHash ?? ''
60+
hash: prevHash => prevHash ?? '',
61+
state: state => state
6962
})
7063
})
7164
},
72-
[navigate, from]
65+
[navigate, pathname]
7366
)
7467

7568
return {

0 commit comments

Comments
 (0)