Skip to content

Commit

Permalink
ref: Enable history patching by default for React Router
Browse files Browse the repository at this point in the history
It's needed to follow <Link> navigation, which is pretty essential.
  • Loading branch information
franky47 committed Dec 31, 2024
1 parent 2766d05 commit 87276ce
Show file tree
Hide file tree
Showing 10 changed files with 33 additions and 46 deletions.
30 changes: 9 additions & 21 deletions packages/docs/content/docs/options.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -102,27 +102,15 @@ function Component() {
This concept of _"shallow routing"_ is done via updates to the browser's
[History API](https://developer.mozilla.org/en-US/docs/Web/API/History_API/Working_with_the_History_API).

While the `useOptimisticSearchParams` and the adapter itself can handle shallow URL
updates triggered from state updater functions, for them to react to URL changes
triggered by explicit calls to the History API (either by first or third party code),
you'd have to enable sync:

```tsx
// Export available in:
// 'nuqs/adapters/remix'
// 'nuqs/adapters/react-router/v6'
// 'nuqs/adapters/react-router/v7'
// 'nuqs/adapters/react'
import { enableHistorySync } from 'nuqs/adapters/remix'

// Somewhere top-level (like app/root.tsx)
enableHistorySync()
```

Note that you may not need this if only using your framework's router.

It is opt-in as it patches the History APIs, which can have side effects
if third party code does it too.
<Callout title="Why not using shouldRevalidate?">
[`shouldRevalidate`](https://reactrouter.com/start/framework/route-module#shouldrevalidate)
is the idomatic way of opting out of running loaders on navigation, but nuqs uses
the opposite approach: opting in to running loaders only when needed.

In order to avoid specifying `shouldRevalidate` for every route, nuqs chose to
patch the history methods to enable shallow routing by default (on its own updates)
in React Router based frameworks.
</Callout>

## Scroll

Expand Down
4 changes: 1 addition & 3 deletions packages/e2e/react-router/v6/src/react-router.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { enableHistorySync, NuqsAdapter } from 'nuqs/adapters/react-router/v6'
import { NuqsAdapter } from 'nuqs/adapters/react-router/v6'
import {
createBrowserRouter,
createRoutesFromElements,
Expand All @@ -7,8 +7,6 @@ import {
} from 'react-router-dom'
import RootLayout from './layout'

enableHistorySync()

// Adapt the RRv7 / Remix default export for component into a Component export for v6
function load(mod: Promise<{ default: any; [otherExports: string]: any }>) {
return () =>
Expand Down
4 changes: 1 addition & 3 deletions packages/e2e/react-router/v7/app/root.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { enableHistorySync, NuqsAdapter } from 'nuqs/adapters/react-router/v7'
import { NuqsAdapter } from 'nuqs/adapters/react-router/v7'
import {
isRouteErrorResponse,
Links,
Expand All @@ -8,8 +8,6 @@ import {
ScrollRestoration
} from 'react-router'

enableHistorySync()

import type { Route } from './+types/root'

export function Layout({ children }: { children: React.ReactNode }) {
Expand Down
4 changes: 1 addition & 3 deletions packages/e2e/remix/app/root.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
import { Links, Meta, Scripts, ScrollRestoration } from '@remix-run/react'
import { enableHistorySync, NuqsAdapter } from 'nuqs/adapters/remix'
import { NuqsAdapter } from 'nuqs/adapters/remix'
import RootLayout from './layout'

enableHistorySync()

export function Layout({ children }: { children: React.ReactNode }) {
return (
<html lang="en">
Expand Down
12 changes: 12 additions & 0 deletions packages/nuqs/src/adapters/lib/patch-history.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,12 +51,24 @@ export function patchHistory(
if (history.nuqs?.adapters?.includes(adapter)) {
return
}
let lastSearchSeen = typeof location === 'object' ? location.search : ''

emitter.on('update', search => {
lastSearchSeen = search.toString()
})

debug(
'[nuqs %s] Patching history (%s adapter)',
'0.0.0-inject-version-here',
adapter
)
function sync(url: URL | string) {
try {
const newSearch = new URL(url, location.origin).search
if (newSearch === lastSearchSeen) {
return
}
} catch {}
try {
emitter.emit('update', getSearchParams(url))
} catch (e) {
Expand Down
3 changes: 0 additions & 3 deletions packages/nuqs/src/adapters/lib/react-router.ts
Original file line number Diff line number Diff line change
Expand Up @@ -92,9 +92,6 @@ export function createReactRouterBasedAdapter(
window.removeEventListener('popstate', onPopState)
}
}, [])
useEffect(() => {
emitter.emit('update', serverSearchParams)
}, [serverSearchParams])
return searchParams
}
/**
Expand Down
10 changes: 0 additions & 10 deletions packages/nuqs/src/adapters/react-router.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,4 @@
export {
/**
* @deprecated This import will be removed in [email protected].
*
* Please pin your version of React Router in the import:
* - `nuqs/adapters/react-router/v6`
* - `nuqs/adapters/react-router/v7`.
*
* Note: this deprecated import (`nuqs/adapters/react-router`) is for React Router v6 only.
*/
enableHistorySync,
/**
* @deprecated This import will be removed in [email protected].
*
Expand Down
4 changes: 3 additions & 1 deletion packages/nuqs/src/adapters/react-router/v6.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ const {
useSearchParams
)

export { enableHistorySync, useOptimisticSearchParams }
export { useOptimisticSearchParams }

export const NuqsAdapter = createAdapterProvider(useNuqsReactRouterV6Adapter)

enableHistorySync()
4 changes: 3 additions & 1 deletion packages/nuqs/src/adapters/react-router/v7.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ const {
useSearchParams
)

export { enableHistorySync, useOptimisticSearchParams }
export { useOptimisticSearchParams }

export const NuqsAdapter = createAdapterProvider(useNuqsReactRouterV7Adapter)

enableHistorySync()
4 changes: 3 additions & 1 deletion packages/nuqs/src/adapters/remix.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ const {
useOptimisticSearchParams
} = createReactRouterBasedAdapter('remix', useNavigate, useSearchParams)

export { enableHistorySync, useOptimisticSearchParams }
export { useOptimisticSearchParams }

export const NuqsAdapter = createAdapterProvider(useNuqsRemixAdapter)

enableHistorySync()

0 comments on commit 87276ce

Please sign in to comment.