Skip to content

Commit 7f1b18a

Browse files
authored
fix: Conditional rendering in React Router (#882)
1 parent d0c18e1 commit 7f1b18a

23 files changed

+213
-3
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import { testConditionalRendering } from 'e2e-shared/specs/conditional-rendering.cy'
2+
3+
testConditionalRendering({
4+
path: '/app/conditional-rendering/useQueryState',
5+
hook: 'useQueryState',
6+
nextJsRouter: 'app'
7+
})
8+
9+
testConditionalRendering({
10+
path: '/app/conditional-rendering/useQueryStates',
11+
hook: 'useQueryStates',
12+
nextJsRouter: 'app'
13+
})
14+
15+
testConditionalRendering({
16+
path: '/pages/conditional-rendering/useQueryState',
17+
hook: 'useQueryState',
18+
nextJsRouter: 'pages'
19+
})
20+
21+
testConditionalRendering({
22+
path: '/pages/conditional-rendering/useQueryStates',
23+
hook: 'useQueryStates',
24+
nextJsRouter: 'pages'
25+
})
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import { ConditionalRenderingUseQueryState } from 'e2e-shared/specs/conditional-rendering'
2+
import { Suspense } from 'react'
3+
4+
export default function Page() {
5+
return (
6+
<Suspense>
7+
<ConditionalRenderingUseQueryState />
8+
</Suspense>
9+
)
10+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import { ConditionalRenderingUseQueryStates } from 'e2e-shared/specs/conditional-rendering'
2+
import { Suspense } from 'react'
3+
4+
export default function Page() {
5+
return (
6+
<Suspense>
7+
<ConditionalRenderingUseQueryStates />
8+
</Suspense>
9+
)
10+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import { ConditionalRenderingUseQueryState } from 'e2e-shared/specs/conditional-rendering'
2+
3+
export default ConditionalRenderingUseQueryState
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import { ConditionalRenderingUseQueryStates } from 'e2e-shared/specs/conditional-rendering'
2+
3+
export default ConditionalRenderingUseQueryStates
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import { testConditionalRendering } from 'e2e-shared/specs/conditional-rendering.cy'
2+
3+
testConditionalRendering({
4+
path: '/conditional-rendering/useQueryState',
5+
hook: 'useQueryState'
6+
})
7+
8+
testConditionalRendering({
9+
path: '/conditional-rendering/useQueryStates',
10+
hook: 'useQueryStates'
11+
})

packages/e2e/react-router/v6/src/react-router.tsx

+2
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,8 @@ const router = createBrowserRouter(
4141
<Route path="form/useQueryStates" lazy={load(import('./routes/form.useQueryStates'))} />
4242
<Route path="referential-stability/useQueryState" lazy={load(import('./routes/referential-stability.useQueryState'))} />
4343
<Route path="referential-stability/useQueryStates" lazy={load(import('./routes/referential-stability.useQueryStates'))} />
44+
<Route path="conditional-rendering/useQueryState" lazy={load(import('./routes/conditional-rendering.useQueryState'))} />
45+
<Route path="conditional-rendering/useQueryStates" lazy={load(import('./routes/conditional-rendering.useQueryStates'))} />
4446

4547
<Route path="render-count/:hook/:shallow/:history/:startTransition/no-loader" lazy={load(import('./routes/render-count.$hook.$shallow.$history.$startTransition.no-loader'))} />
4648
<Route path="render-count/:hook/:shallow/:history/:startTransition/sync-loader" lazy={load(import('./routes/render-count.$hook.$shallow.$history.$startTransition.sync-loader'))} />
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import { ConditionalRenderingUseQueryState } from 'e2e-shared/specs/conditional-rendering'
2+
3+
export default ConditionalRenderingUseQueryState
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import { ConditionalRenderingUseQueryStates } from 'e2e-shared/specs/conditional-rendering'
2+
3+
export default ConditionalRenderingUseQueryStates

packages/e2e/react-router/v7/app/routes.ts

+2
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ export default [
2424
route('/form/useQueryStates', './routes/form.useQueryStates.tsx'),
2525
route('/referential-stability/useQueryState', './routes/referential-stability.useQueryState.tsx'),
2626
route('/referential-stability/useQueryStates', './routes/referential-stability.useQueryStates.tsx'),
27+
route('/conditional-rendering/useQueryState', './routes/conditional-rendering.useQueryState.tsx'),
28+
route('/conditional-rendering/useQueryStates', './routes/conditional-rendering.useQueryStates.tsx'),
2729
route('/render-count/:hook/:shallow/:history/:startTransition/no-loader', './routes/render-count.$hook.$shallow.$history.$startTransition.no-loader.tsx'),
2830
route('/render-count/:hook/:shallow/:history/:startTransition/sync-loader', './routes/render-count.$hook.$shallow.$history.$startTransition.sync-loader.tsx'),
2931
route('/render-count/:hook/:shallow/:history/:startTransition/async-loader', './routes/render-count.$hook.$shallow.$history.$startTransition.async-loader.tsx'),
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import { ConditionalRenderingUseQueryState } from 'e2e-shared/specs/conditional-rendering'
2+
3+
export default ConditionalRenderingUseQueryState
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import { ConditionalRenderingUseQueryStates } from 'e2e-shared/specs/conditional-rendering'
2+
3+
export default ConditionalRenderingUseQueryStates
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import { testConditionalRendering } from 'e2e-shared/specs/conditional-rendering.cy'
2+
3+
testConditionalRendering({
4+
path: '/conditional-rendering/useQueryState',
5+
hook: 'useQueryState'
6+
})
7+
8+
testConditionalRendering({
9+
path: '/conditional-rendering/useQueryStates',
10+
hook: 'useQueryStates'
11+
})
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import { testConditionalRendering } from 'e2e-shared/specs/conditional-rendering.cy'
2+
3+
testConditionalRendering({
4+
path: '/conditional-rendering/useQueryState',
5+
hook: 'useQueryState'
6+
})
7+
8+
testConditionalRendering({
9+
path: '/conditional-rendering/useQueryStates',
10+
hook: 'useQueryStates'
11+
})

packages/e2e/react/src/routes.tsx

+2
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ const routes: Record<string, React.LazyExoticComponent<() => JSX.Element>> = {
2121
'/form/useQueryStates': lazy(() => import('./routes/form.useQueryStates')),
2222
'/referential-stability/useQueryState': lazy(() => import('./routes/referential-stability.useQueryState')),
2323
'/referential-stability/useQueryStates': lazy(() => import('./routes/referential-stability.useQueryStates')),
24+
'/conditional-rendering/useQueryState': lazy(() => import('./routes/conditional-rendering.useQueryState')),
25+
'/conditional-rendering/useQueryStates': lazy(() => import('./routes/conditional-rendering.useQueryStates')),
2426

2527
'/render-count/useQueryState/true/replace/false': lazy(() => import('./routes/render-count')),
2628
'/render-count/useQueryState/true/replace/true': lazy(() => import('./routes/render-count')),
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import { ConditionalRenderingUseQueryState } from 'e2e-shared/specs/conditional-rendering'
2+
3+
export default ConditionalRenderingUseQueryState
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import { ConditionalRenderingUseQueryStates } from 'e2e-shared/specs/conditional-rendering'
2+
3+
export default ConditionalRenderingUseQueryStates
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import { ConditionalRenderingUseQueryState } from 'e2e-shared/specs/conditional-rendering'
2+
3+
export default ConditionalRenderingUseQueryState
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import { ConditionalRenderingUseQueryStates } from 'e2e-shared/specs/conditional-rendering'
2+
3+
export default ConditionalRenderingUseQueryStates
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import { testConditionalRendering } from 'e2e-shared/specs/conditional-rendering.cy'
2+
3+
testConditionalRendering({
4+
path: '/conditional-rendering/useQueryState',
5+
hook: 'useQueryState'
6+
})
7+
8+
testConditionalRendering({
9+
path: '/conditional-rendering/useQueryStates',
10+
hook: 'useQueryStates'
11+
})
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import { createTest } from '../create-test'
2+
3+
export const testConditionalRendering = createTest(
4+
'Conditional rendering',
5+
({ path }) => {
6+
it('should have the correct initial state after mounting', () => {
7+
cy.visit(path + '?test=pass')
8+
cy.contains('#hydration-marker', 'hydrated').should('be.hidden')
9+
cy.get('button#mount').click()
10+
cy.get('#state').should('have.text', 'pass')
11+
})
12+
it('should keep the correct state after unmounting and remounting', () => {
13+
cy.visit(path)
14+
cy.contains('#hydration-marker', 'hydrated').should('be.hidden')
15+
cy.get('button#mount').click()
16+
cy.get('button#set').click()
17+
cy.get('button#unmount').click()
18+
cy.get('button#mount').click()
19+
cy.get('#state').should('have.text', 'pass')
20+
})
21+
}
22+
)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
'use client'
2+
3+
import { parseAsString, useQueryState, useQueryStates } from 'nuqs'
4+
import { type FC, useState } from 'react'
5+
6+
function ConditionalRenderer({ Component }: { Component: FC }) {
7+
const [mounted, setMounted] = useState(false)
8+
return (
9+
<>
10+
<button id="mount" onClick={() => setMounted(true)}>
11+
Mount
12+
</button>
13+
<button id="unmount" onClick={() => setMounted(false)}>
14+
Unount
15+
</button>
16+
{mounted && <Component />}
17+
</>
18+
)
19+
}
20+
21+
// --
22+
23+
function TestComponentUseQueryState() {
24+
const [state, setState] = useQueryState('test')
25+
return (
26+
<>
27+
<button id="set" onClick={() => setState('pass')}>
28+
Set
29+
</button>
30+
<pre id="state">{state}</pre>
31+
</>
32+
)
33+
}
34+
35+
function TestComponentUseQueryStates() {
36+
const [{ state }, setState] = useQueryStates(
37+
{
38+
state: parseAsString
39+
},
40+
{ urlKeys: { state: 'test' } }
41+
)
42+
return (
43+
<>
44+
<button id="set" onClick={() => setState({ state: 'pass' })}>
45+
Set
46+
</button>
47+
<pre id="state">{state}</pre>
48+
</>
49+
)
50+
}
51+
52+
// --
53+
54+
export function ConditionalRenderingUseQueryState() {
55+
return <ConditionalRenderer Component={TestComponentUseQueryState} />
56+
}
57+
58+
export function ConditionalRenderingUseQueryStates() {
59+
return <ConditionalRenderer Component={TestComponentUseQueryStates} />
60+
}

packages/nuqs/src/adapters/lib/react-router.ts

+6-3
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,7 @@ type NavigateOptions = {
2020
}
2121
type NavigateFn = (url: NavigateUrl, options: NavigateOptions) => void
2222
type UseNavigate = () => NavigateFn
23-
24-
type UseSearchParams = () => [URLSearchParams, {}]
23+
type UseSearchParams = (initial: URLSearchParams) => [URLSearchParams, {}]
2524

2625
// --
2726

@@ -78,7 +77,11 @@ export function createReactRouterBasedAdapter(
7877
}
7978
}
8079
function useOptimisticSearchParams() {
81-
const [serverSearchParams] = useSearchParams()
80+
const [serverSearchParams] = useSearchParams(
81+
typeof location === 'undefined'
82+
? new URLSearchParams()
83+
: new URLSearchParams(location.search)
84+
)
8285
const [searchParams, setSearchParams] = useState(serverSearchParams)
8386
useEffect(() => {
8487
function onPopState() {

0 commit comments

Comments
 (0)