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で同じページにナビゲートする場合、デフォルトではページのstateはリセット**されません**。これは、親コンポーネントが変更されない限り、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 の間でナビゲートしても、count はリセット**されません**。useState は、トップレベルのReactコンポーネントである Page が同じであるため、レンダリング間で維持されます。
この動作を望まない場合は、いくつかの選択肢があります。
-
useEffectを使用して、各stateが更新されることを手動で確認します。上記の例では、次のようになります。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
next/link の replace プロパティと同様に、router.replace は history スタックに新しいURLエントリを追加することを防ぎます。
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- ブラウザに表示されるURL。options: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
現在のURLをリロードします。ブラウザのリフレッシュボタンをクリックするのと同じです。window.location.reload() を実行します。
import { useRouter } from 'next/router'
export default function Page() {
const router = useRouter()
return (
<button type="button" onClick={() => router.reload()}>
Click here to reload
</button>
)
}router.events
Next.js Router内で発生するさまざまなイベントをリッスンできます。サポートされているイベントのリストは以下の通りです。
routeChangeStart(url, { shallow })- ルートの変更が開始したときに発生します。routeChangeComplete(url, { shallow })- ルートが完全に変更されたときに発生します。routeChangeError(err, url, { shallow })- ルートの変更中にエラーが発生した場合、またはルートのロードがキャンセルされた場合に発生します。err.cancelled- ナビゲーションがキャンセルされたかどうかを示します。
beforeHistoryChange(url, { shallow })- ブラウザの履歴を変更する前に発生します。hashChangeStart(url, { shallow })- ハッシュは変更されるがページは変更されない場合に発生します。hashChangeComplete(url, { shallow })- ハッシュは変更されたがページは変更されない場合に発生します。
知っておくと良いこと: ここでの
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 とは異なり、pagesルーターがマウントされていない場合にエラーをスローせず、代わりに 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 ディレクトリで使用されなくなった場合は、compat ルーターへの参照を削除できます。
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でuseRouterをNext.jsコンテキスト外で使用する
別の特定のユースケースは、pages ディレクトリの getServerSideProps 内など、Next.jsアプリケーションコンテキスト外でコンポーネントをレンダリングする場合です。この場合、compat ルーターを使用してエラーを回避できます。
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.pushrouter.replacerouter.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 を使用するには、コンポーネントがルータープロップを受け取る必要があります。
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)役に立ちましたか?