Skip to content

Commit

Permalink
feat: Update to useRouter from next/navigation
Browse files Browse the repository at this point in the history
Closes: #327
  • Loading branch information
andreisocaciu authored and franky47 committed Aug 30, 2023
1 parent 3920763 commit a604351
Show file tree
Hide file tree
Showing 27 changed files with 1,114 additions and 954 deletions.
14 changes: 11 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,12 @@

useQueryState hook for Next.js - Like React.useState, but stored in the URL query string

## Migration from v1

In order to use this hook in the new app directory of Next.js 13.4, you need to upgrade to v2.x.

To use this hook in the old pages directory, you need v1.x.

## Features

- 🧘‍♀️ Simple: the URL is the source of truth.
Expand Down Expand Up @@ -176,7 +182,7 @@ useQueryState('foo', { history: 'push' })

Any other value for the `history` option will fallback to the default.

## Multiple Queries
## Multiple Queries (only for v1.x)

Because the Next.js router has asynchronous methods, if you want to do multiple
query updates in one go, you'll have to `await` them, otherwise the latter will
Expand All @@ -193,6 +199,8 @@ const MultipleQueriesDemo = () => {
}
```

> Note: In version 2.x, you don't need to await the state updates.
For query keys that should always move together, you can use `useQueryStates`
with an object containing each key's type:

Expand All @@ -218,9 +226,9 @@ await setCoordinates({
})
```

## Transition Options
## Transition Options (only for v1.x)

By default Next.js will scroll to the top of the page when changing things in the URL.
By default, Next.js will scroll to the top of the page when changing things in the URL.

To prevent this, `router.push()` and `router.replace()` have a third optional
parameter to control transitions, which can be passed on the state setter here:
Expand Down
10 changes: 10 additions & 0 deletions cypress.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { defineConfig } from 'cypress'

export default defineConfig({
e2e: {
baseUrl: 'http://localhost:3000',
video: false,
fixturesFolder: false,
supportFile: false
}
})
7 changes: 0 additions & 7 deletions cypress.json

This file was deleted.

2 changes: 1 addition & 1 deletion cypress/.gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
integration/examples/
e2e/examples/
plugins/
screenshots/
support/
Expand Down
File renamed without changes.
File renamed without changes.
14 changes: 7 additions & 7 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -55,18 +55,18 @@
"devDependencies": {
"@commitlint/config-conventional": "^16.2.1",
"@types/jest": "^27.4.1",
"@types/node": "^17.0.18",
"@types/react": "^17.0.39",
"@types/react-dom": "^17.0.11",
"@types/node": "^18.16.8",
"@types/react": "^18.2.6",
"@types/react-dom": "^18.2.4",
"@types/webpack": "^5.28.0",
"commitlint": "^16.2.1",
"cypress": "^9.5.0",
"cypress": "^12.12.0",
"husky": "7.x",
"jest": "^27.5.1",
"next": "^12.1.0",
"next": "^13.4.2",
"npm-run-all": "^4.1.5",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"ts-jest": "^27.1.3",
"ts-node": "^10.5.0",
"tsd": "^0.19.1",
Expand Down
5 changes: 5 additions & 0 deletions src/app/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# Next.js pages for E2E tests

The Next.js app pages here are dedicated to end-to-end tests with Cypress.

Check out the spec files in `/cypress/e2e`
11 changes: 11 additions & 0 deletions src/app/layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
'use client'

import React from 'react'

export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html>
<body>{children}</body>
</html>
)
}
79 changes: 79 additions & 0 deletions src/app/useQueryState/dynamic/[route]/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
'use client'

import React from 'react'
import { queryTypes, useQueryState } from './../../../../../'

const IntegrationPage = () => {
const [string, setString] = useQueryState('string')
const [int, setInt] = useQueryState('int', queryTypes.integer)
const [float, setFloat] = useQueryState('float', queryTypes.float)
const [bool, setBool] = useQueryState('bool', queryTypes.boolean)
return (
<main>
<h1>useQueryState</h1>
<section>
<h2>String</h2>
<button id="string_set_a" onClick={() => setString('a')}>
Set A
</button>
<button id="string_set_b" onClick={() => setString('b')}>
Set B
</button>
<button id="string_clear" onClick={() => setString(null)}>
Clear
</button>
<p id="string_value">{string}</p>
</section>
<section>
<h2>Integer</h2>
<button
id="int_increment"
onClick={() => setInt(old => (old ?? 0) + 1)}
>
Increment
</button>
<button
id="int_decrement"
onClick={() => setInt(old => (old ?? 0) - 1)}
>
Decrement
</button>
<button id="int_clear" onClick={() => setInt(null)}>
Clear
</button>
<p id="int_value">{int}</p>
</section>
<section>
<h2>Float</h2>
<button
id="float_increment"
onClick={() => setFloat(old => (old ?? 0) + 0.1)}
>
Increment by 0.1
</button>
<button
id="float_decrement"
onClick={() => setFloat(old => (old ?? 0) - 0.1)}
>
Decrement by 0.1
</button>
<button id="float_clear" onClick={() => setFloat(null)}>
Clear
</button>
<p id="float_value">{float}</p>
</section>
<section>
<h2>Boolean</h2>
<button id="bool_toggle" onClick={() => setBool(old => !old)}>
Toggle
</button>
<button id="bool_clear" onClick={() => setBool(null)}>
Clear
</button>
<p id="bool_value">{bool === null ? null : bool ? 'true' : 'false'}</p>
</section>
</main>
)
}

export default IntegrationPage
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import { NextPage } from 'next'
'use client'

import React from 'react'
import { queryTypes, useQueryState } from '../../../'

const IntegrationPage: NextPage = () => {
const IntegrationPage = () => {
const [string, setString] = useQueryState('string')
const [int, setInt] = useQueryState('int', queryTypes.integer)
const [float, setFloat] = useQueryState('float', queryTypes.float)
Expand Down
51 changes: 51 additions & 0 deletions src/app/useQueryStates/dynamic/[route]/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
'use client'

import React from 'react'
import { queryTypes, useQueryStates } from './../../../../../'

const IntegrationPage = () => {
const [state, setState] = useQueryStates({
string: queryTypes.string,
int: queryTypes.integer,
float: queryTypes.float,
bool: queryTypes.boolean
})
return (
<>
<button onClick={() => setState({ string: 'Hello' })}>Set string</button>
<button onClick={() => setState({ int: 42 })}>Set int</button>
<button onClick={() => setState({ float: 3.14159 })}>Set float</button>
<button onClick={() => setState(old => ({ bool: !old.bool }))}>
Toggle bool
</button>
<button
id="clear-string"
onClick={() => setState(() => ({ string: null }))}
>
Clear string
</button>
<button
id="clear"
onClick={() =>
setState(() => ({
string: null,
int: null,
float: null,
bool: null
}))
}
>
Clear
</button>
<p id="json">{JSON.stringify(state)}</p>
<p id="string">{state.string}</p>
<p id="int">{state.int}</p>
<p id="float">{state.float}</p>
<p id="bool">
{state.bool === null ? null : state.bool ? 'true' : 'false'}
</p>
</>
)
}

export default IntegrationPage
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import { NextPage } from 'next'
'use client'

import React from 'react'
import { queryTypes, useQueryStates } from '../../../'

const IntegrationPage: NextPage = () => {
const IntegrationPage = () => {
const [state, setState] = useQueryStates({
string: queryTypes.string,
int: queryTypes.integer,
Expand Down
6 changes: 0 additions & 6 deletions src/defs.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,3 @@
import type { Router } from 'next/router'

// Next.js does not export the TransitionsOption interface,
// but we can get it from where it's used:
export type TransitionOptions = Parameters<Router['push']>[2]

export type HistoryOptions = 'replace' | 'push'

export type Nullable<T> = {
Expand Down
5 changes: 0 additions & 5 deletions src/pages/README.md

This file was deleted.

1 change: 0 additions & 1 deletion src/pages/useQueryState/[...dynamicRoute].tsx

This file was deleted.

1 change: 0 additions & 1 deletion src/pages/useQueryStates/[...dynamicRoute].tsx

This file was deleted.

4 changes: 0 additions & 4 deletions src/tests/useQueryState.test-d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,6 @@ import { queryTypes, useQueryState } from '../index'
expectType<string | null>(state)
setState('bar')
setState(old => old?.toUpperCase() ?? null)
const out = await setState('bar')
expectType<boolean>(out)
}

// Accept only a single `history` option
Expand All @@ -17,8 +15,6 @@ import { queryTypes, useQueryState } from '../index'
expectType<string | null>(state)
setState('bar')
setState(old => old?.toUpperCase() ?? null)
const out = await setState('bar')
expectType<boolean>(out)
}

// Supported query types
Expand Down
2 changes: 0 additions & 2 deletions src/tests/useQueryStates.test-d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,6 @@ import { queryTypes, useQueryStates } from '../index'
...old,
d: !old.d
}))
const out = await setStates({ b: 42 })
expectType<boolean>(out)
}

// With default values, state is no longer nullable
Expand Down
Loading

0 comments on commit a604351

Please sign in to comment.