From caccba3306a159ce2aa24b5b3a34db1510a10403 Mon Sep 17 00:00:00 2001 From: Wyatt Johnson Date: Fri, 4 Nov 2022 12:49:04 -0600 Subject: [PATCH 1/2] feat: added `next/compat/router`, removed `throwOnMissing` option from `next/router` --- errors/manifest.json | 4 ++++ errors/next-router-not-mounted.md | 13 +++++++++++++ packages/next/client/compat/router.ts | 17 +++++++++++++++++ packages/next/client/route-announcer.tsx | 2 +- packages/next/client/router.ts | 10 +++++----- packages/next/compat/router.d.ts | 1 + packages/next/compat/router.js | 1 + packages/next/tsconfig.json | 1 + test/integration/typescript/pages/hello.tsx | 2 +- 9 files changed, 44 insertions(+), 7 deletions(-) create mode 100644 errors/next-router-not-mounted.md create mode 100644 packages/next/client/compat/router.ts create mode 100644 packages/next/compat/router.d.ts create mode 100644 packages/next/compat/router.js diff --git a/errors/manifest.json b/errors/manifest.json index 5e48d7fa9d44..9be351338750 100644 --- a/errors/manifest.json +++ b/errors/manifest.json @@ -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" } ] } diff --git a/errors/next-router-not-mounted.md b/errors/next-router-not-mounted.md new file mode 100644 index 000000000000..a4020d55a6a7 --- /dev/null +++ b/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 plumbed into Next.js. + +#### 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) diff --git a/packages/next/client/compat/router.ts b/packages/next/client/compat/router.ts new file mode 100644 index 000000000000..58b1b9f02ed0 --- /dev/null +++ b/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) +} diff --git a/packages/next/client/route-announcer.tsx b/packages/next/client/route-announcer.tsx index cbd26f09ea7b..3b59fd84d96b 100644 --- a/packages/next/client/route-announcer.tsx +++ b/packages/next/client/route-announcer.tsx @@ -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 diff --git a/packages/next/client/router.ts b/packages/next/client/router.ts index dbcd11a9b2d6..7f4acbd51a60 100644 --- a/packages/next/client/router.ts +++ b/packages/next/client/router.ts @@ -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 diff --git a/packages/next/compat/router.d.ts b/packages/next/compat/router.d.ts new file mode 100644 index 000000000000..c45847372173 --- /dev/null +++ b/packages/next/compat/router.d.ts @@ -0,0 +1 @@ +export * from '../dist/client/compat/router' diff --git a/packages/next/compat/router.js b/packages/next/compat/router.js new file mode 100644 index 000000000000..1b46d4605327 --- /dev/null +++ b/packages/next/compat/router.js @@ -0,0 +1 @@ +module.exports = require('../dist/client/compat/router') diff --git a/packages/next/tsconfig.json b/packages/next/tsconfig.json index abbb87416c5f..0cbb6fd2199e 100644 --- a/packages/next/tsconfig.json +++ b/packages/next/tsconfig.json @@ -14,6 +14,7 @@ "./*.d.ts", "future/*.d.ts", "image-types/global.d.ts", + "compat/*.d.ts", "legacy/*.d.ts", "types/compiled.d.ts" ] diff --git a/test/integration/typescript/pages/hello.tsx b/test/integration/typescript/pages/hello.tsx index 1e5c82d05ec5..acfd061ee410 100644 --- a/test/integration/typescript/pages/hello.tsx +++ b/test/integration/typescript/pages/hello.tsx @@ -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) From d6dbb3570a69af09768cf97fa3428195a8c02a1b Mon Sep 17 00:00:00 2001 From: JJ Kasper Date: Mon, 7 Nov 2022 09:44:45 -0800 Subject: [PATCH 2/2] Apply suggestions from code review --- errors/next-router-not-mounted.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/errors/next-router-not-mounted.md b/errors/next-router-not-mounted.md index a4020d55a6a7..0bdb3f7a9ce7 100644 --- a/errors/next-router-not-mounted.md +++ b/errors/next-router-not-mounted.md @@ -2,7 +2,7 @@ #### 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 plumbed into Next.js. +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