useRouter
アプリ内の任意の関数コンポーネント内でrouter
オブジェクトにアクセスしたい場合は、 useRouter
フックを使用できます。次の例をご覧ください。
import { useRouter } from 'next/router'
function ActiveLink({ children, href }) {
const router = useRouter()
const style = {
marginRight: 10,
color: router.asPath === href ? 'red' : 'black',
}
const handleClick = (e) => {
e.preventDefault()
router.push(href)
}
return (
<a href={href} onClick={handleClick} style={style}>
{children}
</a>
)
}
export default ActiveLink
useRouter
は React Hookであるため、クラスでは使用できません。withRouterを使用するか、クラスを関数コンポーネントでラップする必要があります。
router
オブジェクト
以下は、useRouter
とwithRouter
の両方によって返されるrouter
オブジェクトの定義です。
pathname
:String
-/pages
の後にある現在のルートファイルのパス。したがって、basePath
、locale
、および末尾のスラッシュ(trailingSlash: true
)は含まれません。query
:Object
- 動的ルートパラメータを含む、オブジェクトに解析されたクエリ文字列。ページがサーバーサイドレンダリングを使用しない場合、プリレンダリング中は空のオブジェクトになります。デフォルトは{}
です。asPath
:String
- 検索パラメータを含み、trailingSlash
設定を尊重する、ブラウザに表示されるパス。basePath
とlocale
は含まれません。isFallback
:boolean
- 現在のページがフォールバックモードであるかどうか。basePath
:String
- アクティブなbasePath(有効な場合)。locale
:String
- アクティブなロケール(有効な場合)。locales
:String[]
- すべてのサポートされているロケール(有効な場合)。defaultLocale
:String
- 現在のデフォルトロケール(有効な場合)。domainLocales
:Array<{domain, defaultLocale, locales}>
- 設定されているドメインロケール。isReady
:boolean
- ルータフィールドがクライアント側で更新され、使用できる状態になっているかどうか。useEffect
メソッド内でのみ使用し、サーバーでの条件付きレンダリングには使用しないでください。自動的に静的に最適化されたページの使用例については、関連ドキュメントを参照してください。isPreview
:boolean
- アプリケーションが現在プレビューモードであるかどうか。
ページがサーバーサイドレンダリングまたは自動静的最適化を使用してレンダリングされる場合、
asPath
フィールドを使用すると、クライアントとサーバーの間に不一致が生じる可能性があります。isReady
フィールドがtrue
になるまで、asPath
の使用は避けてください。
router
には次のメソッドが含まれています。
router.push
クライアントサイドの遷移を処理します。このメソッドは、next/link
では不十分な場合に役立ちます。
router.push(url, as, options)
url
:UrlObject | String
- 移動先のURL(UrlObject
のプロパティについては、Node.JS URLモジュールドキュメントを参照してください)。as
:UrlObject | String
- ブラウザのURLバーに表示されるパスのオプションのデコレータ。Next.js 9.5.3より前は、これは動的ルートに使用されていました。options
- 以下の設定オプションを持つオプションのオブジェクトscroll
- オプションのブール値。ナビゲーション後にページの先頭にスクロールするかどうかを制御します。デフォルトはtrue
です。shallow
:getStaticProps
、getServerSideProps
、またはgetInitialProps
を再実行せずに、現在のページのパスを更新します。デフォルトはfalse
です。locale
- オプションの文字列。新しいページのロケールを示します。
外部URLには
router.push
を使用する必要はありません。そのような場合は、window.locationの方が適しています。
事前定義されたルートであるpages/about.js
への移動
import { useRouter } from 'next/router'
export default function Page() {
const router = useRouter()
return (
<button type="button" onClick={() => router.push('/about')}>
Click me
</button>
)
}
動的ルートであるpages/post/[pid].js
への移動
import { useRouter } from 'next/router'
export default function Page() {
const router = useRouter()
return (
<button type="button" onClick={() => router.push('/post/abc')}>
Click me
</button>
)
}
認証が必要なページに役立つ、ユーザーをpages/login.js
にリダイレクトする
import { useEffect } from 'react'
import { useRouter } from 'next/router'
// Here you would fetch and return the user
const useUser = () => ({ user: null, loading: false })
export default function Page() {
const { user, loading } = useUser()
const router = useRouter()
useEffect(() => {
if (!(user || loading)) {
router.push('/login')
}
}, [user, loading])
return <p>Redirecting...</p>
}
ナビゲーション後の状態のリセット
Next.jsで同じページに移動する場合、親コンポーネントが変更されない限りReactはアンマウントされないため、ページの状態はデフォルトではリセットされ**ません**。
import Link from 'next/link'
import { useState } from 'react'
import { useRouter } from 'next/router'
export default function Page(props) {
const router = useRouter()
const [count, setCount] = useState(0)
return (
<div>
<h1>Page: {router.query.slug}</h1>
<p>Count: {count}</p>
<button onClick={() => setCount(count + 1)}>Increase count</button>
<Link href="/one">one</Link> <Link href="/two">two</Link>
</div>
)
}
上記の例では、/one
と/two
の間を移動してもカウントはリセットされ**ません**。トップレベルのReactコンポーネントであるPage
は同じであるため、レンダリング間でuseState
は維持されます。
この動作を望まない場合は、いくつかのオプションがあります。
-
useEffect
を使用して、各状態が更新されるように手動で確認します。上記の例では、次のようになります。useEffect(() => { setCount(0) }, [router.query.slug])
-
React
key
を使用して、Reactにコンポーネントを再マウントするように指示します。すべてのページでこれを行うには、カスタムアプリを使用できます。pages/_app.jsimport { useRouter } from 'next/router' export default function MyApp({ Component, pageProps }) { const router = useRouter() return <Component key={router.asPath} {...pageProps} /> }
URLオブジェクトを使用する
next/link
に使用できるのと同じ方法で、URLオブジェクトを使用できます。 url
とas
の両方のパラメータで機能します。
import { useRouter } from 'next/router'
export default function ReadMore({ post }) {
const router = useRouter()
return (
<button
type="button"
onClick={() => {
router.push({
pathname: '/post/[pid]',
query: { pid: post.id },
})
}}
>
Click here to read more
</button>
)
}
router.replaceのreplace
プロパティと同様に、router.replace
はhistory
スタックに新しいURLエントリが追加されるのを防ぎます。
router.replace(url, as, options)
router.replace
のAPIは、router.push
のAPIとまったく同じです。
router.replace(url, as, options)
router.replace
のAPIは、router.push
のAPIとまったく同じです。次の例をご覧ください。
import { useRouter } from 'next/router'
export default function Page() {
const router = useRouter()
return (
<button type="button" onClick={() => router.replace('/home')}>
Click me
</button>
)
}
router.prefetch
高速なクライアントサイド遷移のためにページをプリフェッチします。 next/link
はページのプリフェッチを自動的に処理するため、このメソッドはnext/link
を使用しないナビゲーションにのみ役立ちます。
これは本番環境のみの機能です。Next.jsは開発環境ではページをプリフェッチしません。
router.prefetch(url, as, options)
url
- プリフェッチするURL。明示的なルート(例:/dashboard
)と動的ルート(例:/product/[id]
)を含みます。as
-url
のオプションのデコレータ。Next.js 9.5.3より前は、これは動的ルートのプリフェッチに使用されていました。options
- 以下の許可されたフィールドを持つオプションのオブジェクトlocale
- アクティブなものとは異なるロケールを提供できます。false
の場合、アクティブなロケールは使用されないため、url
にロケールを含める必要があります。
たとえば、ログインページがあり、ログイン後にユーザーをダッシュボードにリダイレクトするとします。その場合、次の例のように、ダッシュボードをプリフェッチして遷移を高速化できます。
import { useCallback, useEffect } from 'react'
import { useRouter } from 'next/router'
export default function Login() {
const router = useRouter()
const handleSubmit = useCallback((e) => {
e.preventDefault()
fetch('/api/login', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
/* Form data */
}),
}).then((res) => {
// Do a fast client-side transition to the already prefetched dashboard page
if (res.ok) router.push('/dashboard')
})
}, [])
useEffect(() => {
// Prefetch the dashboard page
router.prefetch('/dashboard')
}, [router])
return (
<form onSubmit={handleSubmit}>
{/* Form fields */}
<button type="submit">Login</button>
</form>
)
}
router.beforePopState
場合によっては(たとえば、カスタムサーバーを使用している場合)、popstateをリッスンして、ルーターが動作する前に何かを実行したい場合があります。
router.beforePopState(cb)
cb
- 受信するpopstate
イベントで実行する関数。この関数は、以下のプロパティを持つオブジェクトとしてイベントの状態を受け取ります。url
:String
- 新しい状態のルート。これは通常、page
の名前です。as
:String
- ブラウザに表示されるURLoptions
:Object
- router.pushによって送信される追加オプション
cb
がfalse
を返した場合、Next.jsルーターはpopstate
を処理せず、その場合は自分で処理する必要があります。ファイルシステムルーティングの無効化を参照してください。
次の例のように、beforePopState
を使用してリクエストを操作したり、SSRリフレッシュを強制したりできます。
import { useEffect } from 'react'
import { useRouter } from 'next/router'
export default function Page() {
const router = useRouter()
useEffect(() => {
router.beforePopState(({ url, as, options }) => {
// I only want to allow these two routes!
if (as !== '/' && as !== '/other') {
// Have SSR render bad routes as a 404.
window.location.href = as
return false
}
return true
})
}, [router])
return <p>Welcome to the page</p>
}
router.back
履歴を戻る。ブラウザの戻るボタンをクリックするのと同じです。 window.history.back()
を実行します。
import { useRouter } from 'next/router'
export default function Page() {
const router = useRouter()
return (
<button type="button" onClick={() => router.back()}>
Click here to go back
</button>
)
}
router.reload
router.events
**知っておくと良いこと**: ここでのurl
は、basePath
を含む、ブラウザに表示されるURLです。
たとえば、ルーターイベントrouteChangeStart
をリッスンするには、pages/_app.js
を開くか作成し、次のようにイベントをサブスクライブします。
import { useEffect } from 'react'
import { useRouter } from 'next/router'
export default function MyApp({ Component, pageProps }) {
const router = useRouter()
useEffect(() => {
const handleRouteChange = (url, { shallow }) => {
console.log(
`App is changing to ${url} ${
shallow ? 'with' : 'without'
} shallow routing`
)
}
router.events.on('routeChangeStart', handleRouteChange)
// If the component is unmounted, unsubscribe
// from the event with the `off` method:
return () => {
router.events.off('routeChangeStart', handleRouteChange)
}
}, [router])
return <Component {...pageProps} />
}
この例では、ページナビゲーションでアンマウントされないため、カスタムアプリ(pages/_app.js
)を使用してイベントをサブスクライブしますが、アプリケーションの任意のコンポーネントでルーターイベントをサブスクライブできます。
ルーターイベントは、コンポーネントがマウントされたときに登録する必要があります(useEffect または componentDidMount / componentWillUnmount )または、イベント発生時に命令的に登録する必要があります。
ルートの読み込みがキャンセルされた場合(たとえば、2つのリンクを連続してクリックした場合)、`routeChangeError` が発生します。渡された `err` には、次の例のように、`cancelled` プロパティが `true` に設定されます。
import { useEffect } from 'react'
import { useRouter } from 'next/router'
export default function MyApp({ Component, pageProps }) {
const router = useRouter()
useEffect(() => {
const handleRouteChangeError = (err, url) => {
if (err.cancelled) {
console.log(`Route to ${url} was cancelled!`)
}
}
router.events.on('routeChangeError', handleRouteChangeError)
// If the component is unmounted, unsubscribe
// from the event with the `off` method:
return () => {
router.events.off('routeChangeError', handleRouteChangeError)
}
}, [router])
return <Component {...pageProps} />
}
`next/compat/router` エクスポート
これは `useRouter` フックと同じですが、`app` ディレクトリと `pages` ディレクトリの両方で使用できます。
`next/router` とは異なり、ページルーターがマウントされていない場合にエラーをスローせず、代わりに `NextRouter | null` の戻り値の型を持ちます。これにより、開発者は `app` ルーターへの移行中に、`app` と `pages` の両方で実行できるようにコンポーネントを変換できます。
以前はこのように見えたコンポーネント
import { useRouter } from 'next/router'
const MyComponent = () => {
const { isReady, query } = useRouter()
// ...
}
`next/compat/router` に変換すると、`null` は分割代入できないため、エラーが発生します。代わりに、開発者は新しいフックを利用できます。
import { useEffect } from 'react'
import { useRouter } from 'next/compat/router'
import { useSearchParams } from 'next/navigation'
const MyComponent = () => {
const router = useRouter() // may be null or a NextRouter instance
const searchParams = useSearchParams()
useEffect(() => {
if (router && !router.isReady) {
return
}
// In `app/`, searchParams will be ready immediately with the values, in
// `pages/` it will be available after the router is ready.
const search = searchParams.get('search')
// ...
}, [router, searchParams])
// ...
}
このコンポーネントは、`pages` ディレクトリと `app` ディレクトリの両方で動作するようになります。コンポーネントが `pages` で使用されなくなったら、互換ルーターへの参照を削除できます。
import { useSearchParams } from 'next/navigation'
const MyComponent = () => {
const searchParams = useSearchParams()
// As this component is only used in `app/`, the compat router can be removed.
const search = searchParams.get('search')
// ...
}
pages で Next.js コンテキスト外で useRouter を使用する
もう1つの具体的なユースケースは、`pages` ディレクトリの `getServerSideProps` 内など、Next.js アプリケーションコンテキスト外でコンポーネントをレンダリングする場合です。この場合、互換ルーターを使用してエラーを回避できます。
import { renderToString } from 'react-dom/server'
import { useRouter } from 'next/compat/router'
const MyComponent = () => {
const router = useRouter() // may be null or a NextRouter instance
// ...
}
export async function getServerSideProps() {
const renderedComponent = renderToString(<MyComponent />)
return {
props: {
renderedComponent,
},
}
}
潜在的な ESLint エラー
`router` オブジェクトでアクセス可能な特定のメソッドは、Promise を返します。ESLint ルール no-floating-promises が有効になっている場合は、グローバルに、または影響を受ける行に対して無効にすることを検討してください。
アプリケーションがこのルールを必要とする場合は、Promise を `void` するか、`async` 関数を使用して Promise を `await` し、関数呼び出しを `void` する必要があります。**これは、メソッドが `onClick` ハンドラー内から呼び出された場合は適用されません**。
影響を受けるメソッドは次のとおりです。
router.push
router.replace
router.prefetch
潜在的な解決策
import { useEffect } from 'react'
import { useRouter } from 'next/router'
// Here you would fetch and return the user
const useUser = () => ({ user: null, loading: false })
export default function Page() {
const { user, loading } = useUser()
const router = useRouter()
useEffect(() => {
// disable the linting on the next line - This is the cleanest solution
// eslint-disable-next-line no-floating-promises
router.push('/login')
// void the Promise returned by router.push
if (!(user || loading)) {
void router.push('/login')
}
// or use an async function, await the Promise, then void the function call
async function handleRouteChange() {
if (!(user || loading)) {
await router.push('/login')
}
}
void handleRouteChange()
}, [user, loading])
return <p>Redirecting...</p>
}
withRouter
`useRouter` が最適でない場合は、`withRouter` を使用して同じ `router` オブジェクト を任意のコンポーネントに追加することもできます。
使用方法
import { withRouter } from 'next/router'
function Page({ router }) {
return <p>{router.pathname}</p>
}
export default withRouter(Page)
TypeScript
`withRouter` でクラスコンポーネントを使用するには、コンポーネントが router プロパティを受け入れる必要があります。
import React from 'react'
import { withRouter, NextRouter } from 'next/router'
interface WithRouterProps {
router: NextRouter
}
interface MyComponentProps extends WithRouterProps {}
class MyComponent extends React.Component<MyComponentProps> {
render() {
return <p>{this.props.router.pathname}</p>
}
}
export default withRouter(MyComponent)
**知っておくと良いこと**: ここでのurl
は、basePath
を含む、ブラウザに表示されるURLです。
たとえば、ルーターイベントrouteChangeStart
をリッスンするには、pages/_app.js
を開くか作成し、次のようにイベントをサブスクライブします。
import { useEffect } from 'react'
import { useRouter } from 'next/router'
export default function MyApp({ Component, pageProps }) {
const router = useRouter()
useEffect(() => {
const handleRouteChange = (url, { shallow }) => {
console.log(
`App is changing to ${url} ${
shallow ? 'with' : 'without'
} shallow routing`
)
}
router.events.on('routeChangeStart', handleRouteChange)
// If the component is unmounted, unsubscribe
// from the event with the `off` method:
return () => {
router.events.off('routeChangeStart', handleRouteChange)
}
}, [router])
return <Component {...pageProps} />
}
この例では、ページナビゲーションでアンマウントされないため、カスタムアプリ(
pages/_app.js
)を使用してイベントをサブスクライブしますが、アプリケーションの任意のコンポーネントでルーターイベントをサブスクライブできます。
ルーターイベントは、コンポーネントがマウントされたときに登録する必要があります(useEffect
ルートの読み込みがキャンセルされた場合(たとえば、2つのリンクを連続してクリックした場合)、`routeChangeError` が発生します。渡された `err` には、次の例のように、`cancelled` プロパティが `true` に設定されます。
import { useEffect } from 'react'
import { useRouter } from 'next/router'
export default function MyApp({ Component, pageProps }) {
const router = useRouter()
useEffect(() => {
const handleRouteChangeError = (err, url) => {
if (err.cancelled) {
console.log(`Route to ${url} was cancelled!`)
}
}
router.events.on('routeChangeError', handleRouteChangeError)
// If the component is unmounted, unsubscribe
// from the event with the `off` method:
return () => {
router.events.off('routeChangeError', handleRouteChangeError)
}
}, [router])
return <Component {...pageProps} />
}
`next/compat/router` エクスポート
これは `useRouter` フックと同じですが、`app` ディレクトリと `pages` ディレクトリの両方で使用できます。
`next/router` とは異なり、ページルーターがマウントされていない場合にエラーをスローせず、代わりに `NextRouter | null` の戻り値の型を持ちます。これにより、開発者は `app` ルーターへの移行中に、`app` と `pages` の両方で実行できるようにコンポーネントを変換できます。
以前はこのように見えたコンポーネント
import { useRouter } from 'next/router'
const MyComponent = () => {
const { isReady, query } = useRouter()
// ...
}
`next/compat/router` に変換すると、`null` は分割代入できないため、エラーが発生します。代わりに、開発者は新しいフックを利用できます。
import { useEffect } from 'react'
import { useRouter } from 'next/compat/router'
import { useSearchParams } from 'next/navigation'
const MyComponent = () => {
const router = useRouter() // may be null or a NextRouter instance
const searchParams = useSearchParams()
useEffect(() => {
if (router && !router.isReady) {
return
}
// In `app/`, searchParams will be ready immediately with the values, in
// `pages/` it will be available after the router is ready.
const search = searchParams.get('search')
// ...
}, [router, searchParams])
// ...
}
このコンポーネントは、`pages` ディレクトリと `app` ディレクトリの両方で動作するようになります。コンポーネントが `pages` で使用されなくなったら、互換ルーターへの参照を削除できます。
import { useSearchParams } from 'next/navigation'
const MyComponent = () => {
const searchParams = useSearchParams()
// As this component is only used in `app/`, the compat router can be removed.
const search = searchParams.get('search')
// ...
}
pages で Next.js コンテキスト外で useRouter を使用する
もう1つの具体的なユースケースは、`pages` ディレクトリの `getServerSideProps` 内など、Next.js アプリケーションコンテキスト外でコンポーネントをレンダリングする場合です。この場合、互換ルーターを使用してエラーを回避できます。
import { renderToString } from 'react-dom/server'
import { useRouter } from 'next/compat/router'
const MyComponent = () => {
const router = useRouter() // may be null or a NextRouter instance
// ...
}
export async function getServerSideProps() {
const renderedComponent = renderToString(<MyComponent />)
return {
props: {
renderedComponent,
},
}
}
潜在的な ESLint エラー
`router` オブジェクトでアクセス可能な特定のメソッドは、Promise を返します。ESLint ルール no-floating-promises
アプリケーションがこのルールを必要とする場合は、Promise を `void` するか、`async` 関数を使用して Promise を `await` し、関数呼び出しを `void` する必要があります。**これは、メソッドが `onClick` ハンドラー内から呼び出された場合は適用されません**。
影響を受けるメソッドは次のとおりです。
router.push
router.replace
router.prefetch
潜在的な解決策
import { useEffect } from 'react'
import { useRouter } from 'next/router'
// Here you would fetch and return the user
const useUser = () => ({ user: null, loading: false })
export default function Page() {
const { user, loading } = useUser()
const router = useRouter()
useEffect(() => {
// disable the linting on the next line - This is the cleanest solution
// eslint-disable-next-line no-floating-promises
router.push('/login')
// void the Promise returned by router.push
if (!(user || loading)) {
void router.push('/login')
}
// or use an async function, await the Promise, then void the function call
async function handleRouteChange() {
if (!(user || loading)) {
await router.push('/login')
}
}
void handleRouteChange()
}, [user, loading])
return <p>Redirecting...</p>
}
withRouter
`useRouter` が最適でない場合は、`withRouter` を使用して同じ `router` オブジェクト を任意のコンポーネントに追加することもできます。
使用方法
import { withRouter } from 'next/router'
function Page({ router }) {
return <p>{router.pathname}</p>
}
export default withRouter(Page)
TypeScript
`withRouter` でクラスコンポーネントを使用するには、コンポーネントが router プロパティを受け入れる必要があります。
import React from 'react'
import { withRouter, NextRouter } from 'next/router'
interface WithRouterProps {
router: NextRouter
}
interface MyComponentProps extends WithRouterProps {}
class MyComponent extends React.Component<MyComponentProps> {
render() {
return <p>{this.props.router.pathname}</p>
}
}
export default withRouter(MyComponent)