diff --git a/src/app/app/routing-tour/_components/parsers.ts b/src/app/app/routing-tour/_components/parsers.ts new file mode 100644 index 00000000..388e3540 --- /dev/null +++ b/src/app/app/routing-tour/_components/parsers.ts @@ -0,0 +1,4 @@ +import { parseAsInteger, parseAsString } from '../../../../../dist/parsers' + +export const counterParser = parseAsInteger.withDefault(0) +export const fromParser = parseAsString diff --git a/src/app/app/routing-tour/_components/view.tsx b/src/app/app/routing-tour/_components/view.tsx new file mode 100644 index 00000000..ccd20354 --- /dev/null +++ b/src/app/app/routing-tour/_components/view.tsx @@ -0,0 +1,33 @@ +'use client' + +import Link from 'next/link' +import { useQueryState } from '../../../../../dist' +import { counterParser, fromParser } from './parsers' + +type RoutingTourViewProps = { + thisPage: string + nextPage: string +} + +export const RoutingTourView: React.FC = ({ + thisPage, + nextPage +}) => { + const [counter] = useQueryState('counter', counterParser) + const [from] = useQueryState('from', fromParser) + return ( + <> + + Next + +

Came from: {from}

+

This page: {thisPage}

+

Next page: {nextPage}

+

Counter: {counter}

+ + ) +} diff --git a/src/app/app/routing-tour/a/page.tsx b/src/app/app/routing-tour/a/page.tsx new file mode 100644 index 00000000..a605a4c0 --- /dev/null +++ b/src/app/app/routing-tour/a/page.tsx @@ -0,0 +1,10 @@ +import { Suspense } from 'react' +import { RoutingTourView } from '../_components/view' + +export default function PageA() { + return ( + + + + ) +} diff --git a/src/app/app/routing-tour/b/page.tsx b/src/app/app/routing-tour/b/page.tsx new file mode 100644 index 00000000..41486b77 --- /dev/null +++ b/src/app/app/routing-tour/b/page.tsx @@ -0,0 +1,10 @@ +import { Suspense } from 'react' +import { RoutingTourView } from '../_components/view' + +export default function PageB() { + return ( + + + + ) +} diff --git a/src/app/app/routing-tour/c/page.tsx b/src/app/app/routing-tour/c/page.tsx new file mode 100644 index 00000000..a0e19b73 --- /dev/null +++ b/src/app/app/routing-tour/c/page.tsx @@ -0,0 +1,7 @@ +'use client' + +import { RoutingTourView } from '../_components/view' + +export default function PageC() { + return +} diff --git a/src/app/app/routing-tour/d/page.tsx b/src/app/app/routing-tour/d/page.tsx new file mode 100644 index 00000000..897d88c1 --- /dev/null +++ b/src/app/app/routing-tour/d/page.tsx @@ -0,0 +1,7 @@ +'use client' + +import { RoutingTourView } from '../_components/view' + +export default function PageD() { + return +} diff --git a/src/app/app/routing-tour/start/client/page.tsx b/src/app/app/routing-tour/start/client/page.tsx new file mode 100644 index 00000000..9496f5ca --- /dev/null +++ b/src/app/app/routing-tour/start/client/page.tsx @@ -0,0 +1,22 @@ +'use client' + +import Link from 'next/link' + +export default function ServerStartPage() { + return ( +
    +
  • + a (server) +
  • +
  • + b (server) +
  • +
  • + c (client) +
  • +
  • + d (client) +
  • +
+ ) +} diff --git a/src/app/app/routing-tour/start/server/page.tsx b/src/app/app/routing-tour/start/server/page.tsx new file mode 100644 index 00000000..64552aef --- /dev/null +++ b/src/app/app/routing-tour/start/server/page.tsx @@ -0,0 +1,20 @@ +import Link from 'next/link' + +export default function ServerStartPage() { + return ( +
    +
  • + a (server) +
  • +
  • + b (server) +
  • +
  • + c (client) +
  • +
  • + d (client) +
  • +
+ ) +} diff --git a/src/lib/sync.ts b/src/lib/sync.ts index 329a742e..9c026bfb 100644 --- a/src/lib/sync.ts +++ b/src/lib/sync.ts @@ -27,6 +27,7 @@ export function usePatchedHistory() { if (patched) { return } + // console.debug('Patching history') for (const method of ['pushState', 'replaceState'] as const) { const original = window.history[method].bind(window.history) window.history[method] = function nextUseQueryState_patchedHistory( @@ -34,10 +35,12 @@ export function usePatchedHistory() { title: string, url?: string | URL | null ) { + // console.debug(`history.${method}(${url}) ${title} %O`, state) // If someone else than our hooks have updated the URL, // send out a signal for them to sync their internal state. if (title !== NOSYNC_MARKER && url) { const search = new URL(url, location.origin).searchParams + // console.debug(`Triggering sync with ${search.toString()}`) // Here we're delaying application to next tick to avoid: // `Warning: useInsertionEffect must not schedule updates.` // diff --git a/src/lib/useQueryState.ts b/src/lib/useQueryState.ts index 99751d43..762a3720 100644 --- a/src/lib/useQueryState.ts +++ b/src/lib/useQueryState.ts @@ -222,16 +222,18 @@ export function useQueryState( // console.debug(`render ${key}: ${internalState}`) // Sync all hooks together & with external URL changes - React.useEffect(() => { + React.useInsertionEffect(() => { function syncFromURL(search: URLSearchParams) { const value = search.get(key) ?? null const v = value === null ? null : parse(value) // console.debug(`sync ${key}: ${v}`) setInternalState(v) } + // console.debug(`Subscribing to sync for \`${key}\``) emitter.on(key, setInternalState) emitter.on(SYNC_EVENT_KEY, syncFromURL) return () => { + // console.debug(`Unsubscribing from sync for \`${key}\``) emitter.off(key, setInternalState) emitter.off(SYNC_EVENT_KEY, syncFromURL) }