diff --git a/.github/workflows/ci-cd.yml b/.github/workflows/ci-cd.yml index 3ff3517ee..16b404310 100644 --- a/.github/workflows/ci-cd.yml +++ b/.github/workflows/ci-cd.yml @@ -29,6 +29,7 @@ jobs: - '14.0.1' # 14.0.2 is not compatible due to a prefetch issue - latest + - '14.0.5-canary.54' # WHS stabilised include: # 14.0.3 requires the WHS flag - next-version: '14.0.3' diff --git a/packages/e2e/cypress/e2e/repro-359.cy.js b/packages/e2e/cypress/e2e/repro-359.cy.js new file mode 100644 index 000000000..4600f912b --- /dev/null +++ b/packages/e2e/cypress/e2e/repro-359.cy.js @@ -0,0 +1,52 @@ +/// + +it.only('repro-359', () => { + cy.visit('/app/repro-359') + cy.contains('#hydration-marker', 'hydrated').should('be.hidden') + + cy.location('search').should('eq', '') + cy.get('#nuqs-param').should('have.text', 'null') + cy.get('#nuqs-component').should('have.text', '') + cy.get('#nuqss-param').should('have.text', 'null') + cy.get('#nuqss-component').should('have.text', '') + + cy.contains('Component 1 (nuqs)').click() + cy.wait(100) + cy.location('search').should('eq', '?param=comp1&component=comp1') + cy.get('#comp1').should('have.text', 'comp1') + cy.get('#comp2').should('not.exist') + cy.get('#nuqs-param').should('have.text', 'comp1') + cy.get('#nuqs-component').should('have.text', 'comp1') + cy.get('#nuqss-param').should('have.text', 'comp1') + cy.get('#nuqss-component').should('have.text', 'comp1') + + cy.contains('Component 2 (nuqs)').click() + cy.wait(100) + cy.location('search').should('eq', '?param=comp2&component=comp2') + cy.get('#comp1').should('not.exist') + cy.get('#comp2').should('have.text', 'comp2') + cy.get('#nuqs-param').should('have.text', 'comp2') + cy.get('#nuqs-component').should('have.text', 'comp2') + cy.get('#nuqss-param').should('have.text', 'comp2') + cy.get('#nuqss-component').should('have.text', 'comp2') + + cy.contains('Component 1 (nuq+)').click() + cy.wait(100) + cy.location('search').should('eq', '?param=comp1&component=comp1') + cy.get('#comp1').should('have.text', 'comp1') + cy.get('#comp2').should('not.exist') + cy.get('#nuqs-param').should('have.text', 'comp1') + cy.get('#nuqs-component').should('have.text', 'comp1') + cy.get('#nuqss-param').should('have.text', 'comp1') + cy.get('#nuqss-component').should('have.text', 'comp1') + + cy.contains('Component 2 (nuq+)').click() + cy.wait(100) + cy.location('search').should('eq', '?param=comp2&component=comp2') + cy.get('#comp1').should('not.exist') + cy.get('#comp2').should('have.text', 'comp2') + cy.get('#nuqs-param').should('have.text', 'comp2') + cy.get('#nuqs-component').should('have.text', 'comp2') + cy.get('#nuqss-param').should('have.text', 'comp2') + cy.get('#nuqss-component').should('have.text', 'comp2') +}) diff --git a/packages/e2e/src/app/app/repro-359/page.tsx b/packages/e2e/src/app/app/repro-359/page.tsx new file mode 100644 index 000000000..2234d7746 --- /dev/null +++ b/packages/e2e/src/app/app/repro-359/page.tsx @@ -0,0 +1,86 @@ +// https://github.com/47ng/nuqs/issues/359 + +'use client' + +import Link from 'next/link' +import { + parseAsString, + parseAsStringLiteral, + useQueryState, + useQueryStates +} from 'nuqs' + +const paramParser = parseAsString.withDefault('null') +const components = ['comp1', 'comp2'] as const +const componentParser = parseAsStringLiteral(components) + +const Component = (props: React.ComponentProps<'span'>) => { + const [param] = useQueryState('param', paramParser) + return {param} +} + +export default function Wolf359() { + const [param, setParam] = useQueryState('param', paramParser) + const [component, seComponent] = useQueryState('component', componentParser) + const [multiple, setMultiple] = useQueryStates({ + param: paramParser, + component: componentParser + }) + return ( + <> +
+ {component === 'comp1' && } + {component === 'comp2' && } +
+
+ {param} + {component} + {multiple.param} + {multiple.component} +
+
+ + +
+ + +
+ + + ) +} diff --git a/packages/nuqs/src/update-queue.ts b/packages/nuqs/src/update-queue.ts index 4837775be..7c66d622d 100644 --- a/packages/nuqs/src/update-queue.ts +++ b/packages/nuqs/src/update-queue.ts @@ -120,6 +120,7 @@ export function scheduleFlushToURL(router: Router) { declare global { interface Window { next: { + version: string router?: NextRouter & { state: { asPath: string diff --git a/packages/nuqs/src/useQueryState.ts b/packages/nuqs/src/useQueryState.ts index d9e7989fa..6edfbe3a2 100644 --- a/packages/nuqs/src/useQueryState.ts +++ b/packages/nuqs/src/useQueryState.ts @@ -241,6 +241,11 @@ export function useQueryState( ) React.useEffect(() => { + // This will be removed in v2 which will drop support for + // partially-functional shallow routing (14.0.2 and 14.0.3) + if (window.next.version !== '14.0.3') { + return + } const value = initialSearchParams.get(key) ?? null const state = value === null ? null : safeParse(parse, value, key) debug('[nuqs `%s`] syncFromUseSearchParams %O', key, state)