Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

next/compat/router #42502

Merged
merged 3 commits into from Nov 7, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 4 additions & 0 deletions errors/manifest.json
Expand Up @@ -757,6 +757,10 @@
{
"title": "invalid-segment-export",
"path": "/errors/invalid-segment-export.md"
},
{
"title": "next-router-not-mounted",
"path": "/errors/next-router-not-mounted.md"
}
]
}
Expand Down
13 changes: 13 additions & 0 deletions errors/next-router-not-mounted.md
@@ -0,0 +1,13 @@
# NextRouter was not mounted

#### Why This Error Occurred

A component used `useRouter` outside a Next.js application, or was rendered outside a Next.js application. This can happen when doing unit testing on components that use the `useRouter` hook as they are not configured with Next.js' contexts.

#### Possible Ways to Fix It

If used in a test, mock out the router by mocking the `next/router`'s `useRouter()` hook.

### Useful Links

- [next-router-mock](https://www.npmjs.com/package/next-router-mock)
17 changes: 17 additions & 0 deletions packages/next/client/compat/router.ts
@@ -0,0 +1,17 @@
import { useContext } from 'react'
import { RouterContext } from '../../shared/lib/router-context'
import { NextRouter } from '../router'

/**
* useRouter from `next/compat/router` is designed to assist developers
* migrating from `pages/` to `app/`. Unlike `next/router`, this hook does not
* throw when the `NextRouter` is not mounted, and instead returns `null`. The
* more concrete return type here lets developers use this hook within
* components that could be shared between both `app/` and `pages/` and handle
* to the case where the router is not mounted.
*
* @returns The `NextRouter` instance if it's available, otherwise `null`.
*/
export function useRouter(): NextRouter | null {
return useContext(RouterContext)
}
2 changes: 1 addition & 1 deletion packages/next/client/route-announcer.tsx
Expand Up @@ -17,7 +17,7 @@ const nextjsRouteAnnouncerStyles: React.CSSProperties = {
}

export const RouteAnnouncer = () => {
const { asPath } = useRouter(true)
const { asPath } = useRouter()
const [routeAnnouncement, setRouteAnnouncement] = React.useState('')

// Only announce the path change, but not for the first load because screen
Expand Down
10 changes: 5 additions & 5 deletions packages/next/client/router.ts
Expand Up @@ -129,12 +129,12 @@ export default singletonRouter as SingletonRouter
// Reexport the withRoute HOC
export { default as withRouter } from './with-router'

export function useRouter(throwOnMissing: true): NextRouter
export function useRouter(): NextRouter
export function useRouter(throwOnMissing?: boolean) {
export function useRouter(): NextRouter {
const router = React.useContext(RouterContext)
if (!router && throwOnMissing) {
throw new Error('invariant expected pages router to be mounted')
if (!router) {
throw new Error(
'Error: NextRouter was not mounted. https://nextjs.org/docs/messages/next-router-not-mounted'
)
}

return router
Expand Down
1 change: 1 addition & 0 deletions packages/next/compat/router.d.ts
@@ -0,0 +1 @@
export * from '../dist/client/compat/router'
1 change: 1 addition & 0 deletions packages/next/compat/router.js
@@ -0,0 +1 @@
module.exports = require('../dist/client/compat/router')
1 change: 1 addition & 0 deletions packages/next/tsconfig.json
Expand Up @@ -14,6 +14,7 @@
"./*.d.ts",
"future/*.d.ts",
"image-types/global.d.ts",
"compat/*.d.ts",
"legacy/*.d.ts",
"types/compiled.d.ts"
]
Expand Down
2 changes: 1 addition & 1 deletion test/integration/typescript/pages/hello.tsx
Expand Up @@ -31,7 +31,7 @@ class Test2 extends Test {
new Test2().show()

export default function HelloPage(): JSX.Element {
const router = useRouter(true)
const router = useRouter()
console.log(process.browser)
console.log(router.pathname)
console.log(router.isReady)
Expand Down