Skip to content

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> でラップすることです。

jsx
// 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>
);
jsx
// 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> の子孫になるように構成します。

jsx
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 を使用する方法

jsx
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();
});

ルート付きのテスト

jsx
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 から行うように統一してください。

jsx
// 正しいインポート
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> コンポーネントの子孫である必要があります。

jsx
// 間違った例
<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 のコンテキスト外でナビゲーションフックを使用しようとした際に発生します。この問題を解決するには:

  1. アプリケーション全体を <BrowserRouter> でラップする
  2. テスト時は <MemoryRouter> を使用する
  3. すべてのインポートを react-router-dom から行うように統一する
  4. コンポーネント階層を確認し、すべてのルーティング関連コンポーネントが <Router> の子孫になるようにする

これらの対策を行うことで、React Router のナビゲーション機能を正しく使用できるようになります。