useNavigate() が Router コンポーネントのコンテキスト内でのみ使用可能なエラー
問題説明
React Router v6 で useNavigate() フックを使用する際に、以下のエラーが発生することがあります:
useNavigate() may be used only in the context of a <Router> componentこのエラーは、useNavigate() フックが <Router> コンポーネント(<BrowserRouter>, <HashRouter>, <MemoryRouter> など)のコンテキスト外で使用された場合に発生します。
根本原因
useNavigate() フックは内部で React の Context API を使用しており、ルーティング関連の情報を提供する <Router> コンポーネントにラップされている必要があります。
React Router のソースコードでは、useInRouterContext() 関数が現在のコンポーネントが <Router> の子孫であるかどうかをチェックし、そうでない場合にこのエラーをスローします。
解決方法
方法1: アプリケーション全体を Router でラップする
最も一般的な解決策は、アプリケーションのルートコンポーネントを <BrowserRouter> でラップすることです。
// index.js または main.jsx
import React from 'react';
import ReactDOM from 'react-dom/client';
import { BrowserRouter } from 'react-router-dom';
import App from './App';
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<BrowserRouter>
<App />
</BrowserRouter>
);// App.js
import { useNavigate } from 'react-router-dom';
function App() {
const navigate = useNavigate();
return (
<div>
<button onClick={() => navigate(-1)}>戻る</button>
{/* 他のコンポーネント */}
</div>
);
}
export default App;方法2: 階層構造を適切に整理する
すべてのルーティング関連コンポーネントが <Router> の子孫になるように構成します。
function RootComponent() {
const navigate = useNavigate();
return (
<div>
<button onClick={() => navigate(-1)}>戻る</button>
<Nav />
<Routes>
<Route path="/" element={<Home />} />
<Route path="/home" element={<Home />} />
<Route path="/upcoming/:user" element={<Upcoming />} />
<Route path="/record/:user" element={<Record />} />
<Route path="*" element={<NotFound />} />
</Routes>
</div>
);
}
function App() {
return (
<BrowserRouter>
<RootComponent />
</BrowserRouter>
);
}テスト環境での対処法
テスト時の注意点
単体テストで useNavigate() を使用するコンポーネントをテストする場合、明示的に <Router> でラップする必要があります。
MemoryRouter を使用する方法
import { render, screen } from '@testing-library/react';
import { MemoryRouter } from 'react-router-dom';
import MyComponent from './MyComponent';
test('コンポーネントのテスト', () => {
render(
<MemoryRouter>
<MyComponent />
</MemoryRouter>
);
expect(screen.getByText('テキスト')).toBeInTheDocument();
});ルート付きのテスト
import { render, screen } from '@testing-library/react';
import { MemoryRouter, Routes, Route } from 'react-router-dom';
import MyComponent from './MyComponent';
test('ルート付きコンポーネントのテスト', () => {
render(
<MemoryRouter initialEntries={['/path']}>
<Routes>
<Route path="/path" element={<MyComponent />} />
</Routes>
</MemoryRouter>
);
expect(screen.getByText('テキスト')).toBeInTheDocument();
});よくある間違いと解決策
インポート元の不一致
バージョン不一致
異なるバージョンの React Router からインポートすると、このエラーが発生することがあります。すべてのインポートを react-router-dom から行うように統一してください。
// 正しいインポート
import { useNavigate, BrowserRouter, Routes, Route } from 'react-router-dom';
// 間違ったインポート(react-router と react-router-dom を混在させる)
import { useNavigate } from 'react-router'; // ❌
import { BrowserRouter } from 'react-router-dom'; // ❌コンポーネント階層の問題
すべての useNavigate() を使用するコンポーネントは、<Router> コンポーネントの子孫である必要があります。
// 間違った例
<BrowserRouter>
<AuthProvider> {/* useNavigate() を使用 */}
<NavBar />
<Routes>
<Route path="/login" element={<LoginForm />} />
</Routes>
</AuthProvider>
</BrowserRouter>
<OtherComponent /> {/* ここで useNavigate() を使用するとエラー */}
// 正しい例
<BrowserRouter>
<AuthProvider> {/* useNavigate() を使用 */}
<NavBar />
<Routes>
<Route path="/login" element={<LoginForm />} />
</Routes>
<OtherComponent /> {/* Router の内側なので useNavigate() 使用可能 */}
</AuthProvider>
</BrowserRouter>まとめ
useNavigate() may be used only in the context of a <Router> component エラーは、React Router のコンテキスト外でナビゲーションフックを使用しようとした際に発生します。この問題を解決するには:
- アプリケーション全体を
<BrowserRouter>でラップする - テスト時は
<MemoryRouter>を使用する - すべてのインポートを
react-router-domから行うように統一する - コンポーネント階層を確認し、すべてのルーティング関連コンポーネントが
<Router>の子孫になるようにする
これらの対策を行うことで、React Router のナビゲーション機能を正しく使用できるようになります。