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- クエリ文字列をオブジェクトにパースしたもの。動的ルート パラメータを含みます。ページがサーバーサイドレンダリング (SSR) を使用していない場合、プリレンダリング中は空のオブジェクトになります。デフォルトは{}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- オプションのブール値、ナビゲーション後にページの先頭にスクロールするかどうかを制御します。デフォルトはtrueshallow:getStaticProps、getServerSideProps、getInitialPropsを再実行せずに現在のページのパスを更新します。デフォルトはfalselocale- オプションの文字列、新しいページのロケールを示します
外部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')}>
クリックしてください
</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')}>
クリックしてください
</button>
)
}認証 の背後にあるページにユーザーをリダイレクト(pages/login.js にリダイレクト):
import { useEffect } from 'react'
import { useRouter } from 'next/router'
// ここでユーザーを取得して返す
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>リダイレクト中...</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>ページ: {router.query.slug}</h1>
<p>カウント: {count}</p>
<button onClick={() => setCount(count + 1)}>カウントを増やす</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.js import { 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 },
})
}}
>
続きを読む
</button>
)
}router.replace
next/link の replace プロップと同様に、router.replace は history スタックに新しいURLエントリを追加しません。
router.replace(url, as, options)router.replaceのAPIはrouter.pushとまったく同じです。
以下の例をご覧ください:
import { useRouter } from 'next/router'
export default function Page() {
const router = useRouter()
return (
<button type="button" onClick={() => router.replace('/home')}>
クリックしてください
</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({
/* フォームデータ */
}),
}).then((res) => {
// すでにプリフェッチされたダッシュボードページに高速クライアントサイド遷移
if (res.ok) router.push('/dashboard')
})
}, [])
useEffect(() => {
// ダッシュボードページをプリフェッチ
router.prefetch('/dashboard')
}, [router])
return (
<form onSubmit={handleSubmit}>
{/* フォームフィールド */}
<button type="submit">ログイン</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 }) => {
// これらの2つのルートのみを許可したい!
if (as !== '/' && as !== '/other') {
// 不正なルートをSSRで404としてレンダリング
window.location.href = as
return false
}
return true
})
}, [router])
return <p>ページへようこそ</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()}>
戻る
</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()}>
再読み込み
</button>
)
}router.events
Next.jsルーター内で発生するさまざまなイベントをリッスンできます。サポートされているイベントのリスト:
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(
`アプリが ${url} に変更されます ${
shallow ? 'シャロー' : '非シャロー'
} ルーティング`
)
}
router.events.on('routeChangeStart', handleRouteChange)
// コンポーネントがアンマウントされたら、
// `off` メソッドでイベントの購読を解除:
return () => {
router.events.off('routeChangeStart', handleRouteChange)
}
}, [router])
return <Component {...pageProps} />
}この例ではカスタムApp (
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(`${url} へのルートがキャンセルされました!`)
}
}
router.events.on('routeChangeError', handleRouteChangeError)
// コンポーネントがアンマウントされたら、
// `off` メソッドでイベントの購読を解除:
return () => {
router.events.off('routeChangeError', handleRouteChangeError)
}
}, [router])
return <Component {...pageProps} />
}潜在的なESLintエラー
routerオブジェクトで利用可能な特定のメソッドはPromiseを返します。no-floating-promisesというESLintルールが有効になっている場合、このルールをグローバルに無効にするか、該当する行のみで無効にすることを検討してください。
アプリケーションでこのルールが必要な場合は、Promiseをvoidで処理するか、async関数を使用してPromiseをawaitし、その後関数呼び出しをvoidで処理してください。ただし、onClickハンドラ内からメソッドが呼び出される場合は適用されません。
影響を受けるメソッド:
router.pushrouter.replacerouter.prefetch
解決策の例
import { useEffect } from 'react'
import { useRouter } from 'next/router'
// ここでユーザーを取得して返す
const useUser = () => ({ user: null, loading: false })
export default function Page() {
const { user, loading } = useUser()
const router = useRouter()
useEffect(() => {
// 次の行のリンターを無効化 - 最もクリーンな解決策
// eslint-disable-next-line no-floating-promises
router.push('/login')
// router.pushが返すPromiseをvoidで処理
if (!(user || loading)) {
void router.push('/login')
}
// またはasync関数を使用し、Promiseをawaitしてから関数呼び出しをvoidで処理
async function handleRouteChange() {
if (!(user || loading)) {
await router.push('/login')
}
}
void handleRouteChange()
}, [user, loading])
return <p>リダイレクト中...</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)