diff --git a/packages/e2e/next/cypress/e2e/repro-774.cy.js b/packages/e2e/next/cypress/e2e/repro-774.cy.js new file mode 100644 index 000000000..bde2d78d6 --- /dev/null +++ b/packages/e2e/next/cypress/e2e/repro-774.cy.js @@ -0,0 +1,17 @@ +/// + +describe('repro-774', () => { + it('updates internal state on navigation', () => { + cy.visit('/app/repro-774') + cy.contains('#hydration-marker', 'hydrated').should('be.hidden') + cy.get('#trigger-a').click() + cy.get('#value-a').should('have.text', 'a') + cy.get('#value-b').should('be.empty') + cy.get('#link').click() + cy.get('#value-a').should('be.empty') + cy.get('#value-b').should('be.empty') + cy.get('#trigger-b').click() + cy.get('#value-a').should('be.empty') + cy.get('#value-b').should('have.text', 'b') + }) +}) diff --git a/packages/e2e/next/src/app/app/repro-774/page.tsx b/packages/e2e/next/src/app/app/repro-774/page.tsx new file mode 100644 index 000000000..ed90cdf39 --- /dev/null +++ b/packages/e2e/next/src/app/app/repro-774/page.tsx @@ -0,0 +1,41 @@ +'use client' + +import Link from 'next/link' +import { parseAsString, useQueryStates } from 'nuqs' +import { Suspense } from 'react' + +export default function Home() { + return ( + <> + + + + + + ) +} + +const searchParams = { + a: parseAsString.withDefault(''), + b: parseAsString.withDefault('') +} + +function Client() { + const [{ a, b }, setSearchParams] = useQueryStates(searchParams) + return ( + <> + + + {a} + {b} + + ) +} diff --git a/packages/nuqs/src/useQueryStates.ts b/packages/nuqs/src/useQueryStates.ts index 6ece5c74f..849e7315a 100644 --- a/packages/nuqs/src/useQueryStates.ts +++ b/packages/nuqs/src/useQueryStates.ts @@ -133,6 +133,7 @@ export function useQueryStates( queryRef.current, stateRef.current ) + stateRef.current = state setInternalState(state) }, [ Object.values(resolvedUrlKeys) @@ -274,7 +275,7 @@ function parseMap( cachedQuery?: Record, cachedState?: NullableValues ): NullableValues { - return Object.keys(keyMap).reduce((obj, stateKey) => { + return Object.keys(keyMap).reduce((out, stateKey) => { const urlKey = urlKeys?.[stateKey] ?? stateKey const { parse } = keyMap[stateKey]! const queuedQuery = getQueuedValue(urlKey) @@ -283,15 +284,15 @@ function parseMap( ? (searchParams?.get(urlKey) ?? null) : queuedQuery if (cachedQuery && cachedState && cachedQuery[urlKey] === query) { - obj[stateKey as keyof KeyMap] = cachedState[stateKey] ?? null - return obj + out[stateKey as keyof KeyMap] = cachedState[stateKey] ?? null + return out } const value = query === null ? null : safeParse(parse, query, stateKey) - obj[stateKey as keyof KeyMap] = value ?? null + out[stateKey as keyof KeyMap] = value ?? null if (cachedQuery) { cachedQuery[urlKey] = query } - return obj + return out }, {} as NullableValues) }