Skip to content

React Router の useContext が null になるエラーの解決方法

エラーメッセージ

Uncaught TypeError: Cannot destructure property 'basename' of 'React2.useContext(...)' as it is null

このエラーは、React Router を使用してアプリケーションを構築する際によく発生します。具体的には <Link> コンポーネントを使用している場面で、React Router が提供するコンテキストデータにアクセスしようとすると発生します。以下に示すコードがエラーの原因となります:

jsx
import { Link } from 'react-router-dom';
// ...その他のコード...

const Navbar = () => {
  return (
    <nav>
      <Link to="/">ホーム</Link> {/* エラーが発生する箇所 */}
    </nav>
  );
};

エラーの根本原因

エラーが発生する主な原因は React Router のコンテキストプロバイダーがアプリケーションをラップしていないこと です:

  1. react-router-dom のコンポーネント (Link, Routes など) はすべて BrowserRouter 内部で使用されることを前提としています
  2. useContext フックは BrowserRouter が提供するコンテキストからルーティング情報を取得します
  3. BrowserRouter でラップしていない場合、コンテキストの値が null になる
  4. null 値から basename プロパティを分割代入しようとしてエラーが発生する

技術的な説明

React Router v6 では、useContext(ReactRouterContext) を使用してルーティング情報を取得します。BrowserRouter が存在しないと、このコンテキストは null を返し、そのnull値からプロパティを抽出しようとすると型エラーが発生します。

解決方法: BrowserRouter でアプリケーションをラップする

正しい解決策は、アプリケーション全体を BrowserRouter コンポーネントでラップすることです。通常はエントリーポイントファイル (index.js または main.jsx) で実装します。

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 コンポーネント内にナビゲーションコンポーネントを直接配置すると別の問題が発生します:

jsx
// ❌ 間違った配置例
<BrowserRouter>
  <Routes>
    <Navbar /> {/* これはRoutes内部に直接置けない */}
    <Route path="/" element={<HomePage/>}/>
    // ...その他のルート...
  </Routes>
</BrowserRouter>

正しいコンポーネントの配置方法

Routerの内部にありながら、Routesコンポーネントの外部に共通コンポーネントを配置します:

jsx
// ✅ 正しい配置例
<BrowserRouter>
  <Navbar /> {/* Routesの前に配置 */}
  <Routes>
    <Route path="/" element={<HomePage/>} />
    <Route path="/about" element={<AboutPage/>} />
    {/* 他のルート定義 */}
  </Routes>
</BrowserRouter>

実際のアプリケーション例

jsx
// 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コンポーネントをテストする場合も同様にコンテキストプロバイダーでラップする必要があります:

jsx
// コンポーネントテストの例
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();
});

トラブルシューティング

正常に設定しているのにエラーが発生する場合のチェックリスト:

  1. react-router-dom のバージョンが6以上であること(npm list react-router-dom
  2. 複数のRouterコンポーネントがネストされていないこと
  3. BrowserRouter インポートのスペルが正確であること
  4. インポートパスが間違っていないこと (import { BrowserRouter } from 'react-router-dom';)

ベストプラクティス

  • アプリケーション全体を単一の BrowserRouter でラップする
  • ルーター関連コンポーネントをプロバイダーの外で使用しない
  • Reactコンポーネントツリーの最上位近くでルーターを設定する

これらの手順に従うことで、Cannot destructure property 'basename' エラーは解決され、React Routerのナビゲーションが正常に機能するようになります。