React Router の useContext が null になるエラーの解決方法
エラーメッセージ
Uncaught TypeError: Cannot destructure property 'basename' of 'React2.useContext(...)' as it is null
このエラーは、React Router を使用してアプリケーションを構築する際によく発生します。具体的には <Link>
コンポーネントを使用している場面で、React Router が提供するコンテキストデータにアクセスしようとすると発生します。以下に示すコードがエラーの原因となります:
import { Link } from 'react-router-dom';
// ...その他のコード...
const Navbar = () => {
return (
<nav>
<Link to="/">ホーム</Link> {/* エラーが発生する箇所 */}
</nav>
);
};
エラーの根本原因
エラーが発生する主な原因は React Router のコンテキストプロバイダーがアプリケーションをラップしていないこと です:
react-router-dom
のコンポーネント (Link
,Routes
など) はすべてBrowserRouter
内部で使用されることを前提としていますuseContext
フックはBrowserRouter
が提供するコンテキストからルーティング情報を取得しますBrowserRouter
でラップしていない場合、コンテキストの値がnull
になるnull
値からbasename
プロパティを分割代入しようとしてエラーが発生する
技術的な説明
React Router v6 では、useContext(ReactRouterContext)
を使用してルーティング情報を取得します。BrowserRouter
が存在しないと、このコンテキストは null
を返し、そのnull値からプロパティを抽出しようとすると型エラーが発生します。
解決方法: BrowserRouter でアプリケーションをラップする
正しい解決策は、アプリケーション全体を BrowserRouter
コンポーネントでラップすることです。通常はエントリーポイントファイル (index.js
または main.jsx
) で実装します。
// src/main.jsx
import React from 'react';
import ReactDOM from 'react-dom/client';
import { BrowserRouter } from 'react-router-dom'; // BrowserRouterをインポート
import App from './App';
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<BrowserRouter> {/* アプリ全体をBrowserRouterでラップ */}
<App />
</BrowserRouter>
);
よくある間違い
以下のように、Routes
コンポーネント内にナビゲーションコンポーネントを直接配置すると別の問題が発生します:
// ❌ 間違った配置例
<BrowserRouter>
<Routes>
<Navbar /> {/* これはRoutes内部に直接置けない */}
<Route path="/" element={<HomePage/>}/>
// ...その他のルート...
</Routes>
</BrowserRouter>
正しいコンポーネントの配置方法
Routerの内部にありながら、Routes
コンポーネントの外部に共通コンポーネントを配置します:
// ✅ 正しい配置例
<BrowserRouter>
<Navbar /> {/* Routesの前に配置 */}
<Routes>
<Route path="/" element={<HomePage/>} />
<Route path="/about" element={<AboutPage/>} />
{/* 他のルート定義 */}
</Routes>
</BrowserRouter>
実際のアプリケーション例
// src/App.jsx の例
import { BrowserRouter as Router, Routes, Route } from 'react-router-dom';
import Navbar from './components/Navbar';
import HomePage from './pages/HomePage';
import AboutPage from './pages/AboutPage';
function App() {
return (
<Router>
<div className="App">
<Navbar /> {/* ルートコンポーネントと共通で使用するナビゲーション */}
<Routes>
<Route path="/" element={<HomePage />} />
<Route path="/about" element={<AboutPage />} />
<Route path="*" element={<NotFoundPage />} />
</Routes>
</div>
</Router>
);
}
補足: テスト環境での注意点
テスト環境でReact Routerコンポーネントをテストする場合も同様にコンテキストプロバイダーでラップする必要があります:
// コンポーネントテストの例
import { render } from '@testing-library/react';
import { BrowserRouter } from 'react-router-dom';
import Navbar from './Navbar';
test('ナビゲーションが正しく表示される', () => {
const { getByText } = render(
<BrowserRouter>
<Navbar />
</BrowserRouter>
);
expect(getByText('ホーム')).toBeInTheDocument();
});
トラブルシューティング
正常に設定しているのにエラーが発生する場合のチェックリスト:
react-router-dom
のバージョンが6以上であること(npm list react-router-dom
)- 複数のRouterコンポーネントがネストされていないこと
BrowserRouter
インポートのスペルが正確であること- インポートパスが間違っていないこと (
import { BrowserRouter } from 'react-router-dom';
)
ベストプラクティス
- アプリケーション全体を単一の
BrowserRouter
でラップする - ルーター関連コンポーネントをプロバイダーの外で使用しない
- Reactコンポーネントツリーの最上位近くでルーターを設定する
これらの手順に従うことで、Cannot destructure property 'basename'
エラーは解決され、React Routerのナビゲーションが正常に機能するようになります。