3
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

React Router (react-router-dom) v5のソースコードをv6へ書き換える

Last updated at Posted at 2024-10-29

はじめに

お疲れ様です、りつです!

現在Reactの学習を進めており、React Routerについて以下の動画で学習中です。

非常にわかりやすい教材なのですが、React Routerのバージョンが5のため、現時点の最新バージョンである6を導入している環境だと一部エラーが発生する部分があります。

進める中でエラーが発生した部分や、バージョン6でどのように書き直せばよいのかについてまとめました。

なお、今回StackBlitzを使用しており、各バージョンは以下の状態で進めました。

  • react: 18.3.1
  • react-dom: 18.3.1
  • react-router-dom: 6.27.0

※ 動画の内容をバージョン6の環境で進めており、エラーが発生した際に都度バージョン6で動くよう修正しているため、バージョン5のソースコードがない場合もあります。ご了承ください。

1. 基本的なページ遷移

バージョン6ではSwitchコンポーネントの代わりにRoutesコンポーネントが用意されています。

React Router v6 introduces a Routes component that is kind of like Switch, but a lot more powerful.

注意
バージョン5では<Route exact>という記法がありましたが、バージョン6では廃止されています。
(バージョン6では、特に何かを指定しなくてもパスが完全一致している場合に対象コンポーネントを表示してくれるようです)

バージョン6でSwitchコンポーネントを使用すると以下のようなエラーが発生します。
それぞれのバージョンのサンプルコードも合わせて記載しておりますのでご参照ください。

エラー内容

Uncaught SyntaxError: The requested module '/node_modules/.vite/deps/react-router-dom.js?v=ef39f487' does not provide an export named 'Switch'

image.png

バージョン5の書き方

App.jsx
import { BrowserRouter, Link, Switch, Route } from 'react-router-dom';

import { Home } from './Home';
import { Page1 } from './Page1';
import { Page2 } from './Page2';
import './styles.css';

export default function App() {
  return (
    <BrowserRouter>
      <div className="App">
        <Link to="/">Home</Link>
        <br />
        <Link to="/page1">Page1</Link>
        <br />
        <Link to="/page2">Page2</Link>
      </div>
      <Switch>
        <Route path="/">
          <Home />
        </Route>
        <Route path="page1">
          <Page1 />
        </Route>
        <Route path="page2">
          <Page2 />
        </Route>
      </Switch>
    </BrowserRouter>
  );
}

バージョン6の書き方

App.jsx
import { BrowserRouter, Link, Routes, Route } from 'react-router-dom';

import { Home } from './Home';
import { Page1 } from './Page1';
import { Page2 } from './Page2';
import './styles.css';

export default function App() {
  return (
    <BrowserRouter>
      <div className="App">
        <Link to="/">Home</Link>
        <br />
        <Link to="/page1">Page1</Link>
        <br />
        <Link to="/page2">Page2</Link>
      </div>
      <Routes>
        <Route path="/" element={<Home />}></Route>
        <Route path="/page1" element={<Page1 />}></Route>
        <Route path="/page2" element={<Page2 />}></Route>
      </Routes>
    </BrowserRouter>
  );
}

2. ネストされたページ遷移

遷移先のページで、さらにページ遷移用のリンクを用意する場合の書き方です。

バージョン6の書き方

App.jsx
import { BrowserRouter, Link, Routes, Route } from 'react-router-dom';

import { Home } from './Home';
import { Page1 } from './Page1';
import { Page2 } from './Page2';
import { Page1DetailA } from './Page1DetailA';
import { Page1DetailB } from './Page1DetailB';
import './styles.css';

export default function App() {
  return (
    <BrowserRouter>
      <div className="App">
        <Link to="/">Home</Link>
        <br />
        <Link to="/page1">Page1</Link>
        <br />
        <Link to="/page2">Page2</Link>
      </div>
      <Routes>
        <Route path="/" element={<Home />}></Route>
        <Route path="page1" element={<Page1 />}></Route>
        <Route path="page1/detailA" element={<Page1DetailA />}></Route>
        <Route path="page1/detailB" element={<Page1DetailB />}></Route>
        <Route path="page2" element={<Page2 />}></Route>
      </Routes>
    </BrowserRouter>
  );
}
Page1.jsx
import { Link } from 'react-router-dom';

export const Page1 = () => {
  return (
    <div>
      <h1>Page1ページです</h1>
      <Link to="/page1/detailA">Page1DetailA</Link>
      <br />
      <Link to="/page1/detailB">Page1DetailB</Link>
    </div>
  );
};

結果

Page1リンク押下時

image.png

Page1DetailAリンク押下時

image.png

3. 補足:ネストされたページ遷移(タブ切り替え風)

例えば、Page1DetailAのリンクを押下した際に、それまでのコンポーネントの表示は残したまま、Page1DetailAコンポーネントの内容を表示したい場合はOutletを使用することで実現できます。

When using a nested config, routes with children should render an <Outlet> in order to render their child routes. This makes it easy to render layouts with nested UI.

Outletを使用する際のポイント

  • Routeコンポーネントを入れ子構造にする
  • Routepath属性には、親のルートで指定したパス(今回の例ではpage1)以降のパスを指定する

バージョン6の書き方

App.jsx
import { BrowserRouter, Link, Routes, Route } from 'react-router-dom';

import { Home } from './Home';
import { Page1 } from './Page1';
import { Page2 } from './Page2';
import { Page1DetailA } from './Page1DetailA';
import { Page1DetailB } from './Page1DetailB';
import './styles.css';

export default function App() {
  return (
    <BrowserRouter>
      <div className="App">
        <Link to="/">Home</Link>
        <br />
        <Link to="/page1">Page1</Link>
        <br />
        <Link to="/page2">Page2</Link>
      </div>
      <Routes>
        <Route path="/" element={<Home />}></Route>
        <Route path="page1" element={<Page1 />}>
          <Route path="detailA" element={<Page1DetailA />}></Route>
          <Route path="detailB" element={<Page1DetailB />}></Route>
        </Route>
        <Route path="page2" element={<Page2 />}></Route>
      </Routes>
    </BrowserRouter>
  );
}
Page1.jsx
import { Link, Outlet } from 'react-router-dom';

export const Page1 = () => {
  return (
    <div>
      <h1>Page1ページです</h1>
      <Link to="/page1/detailA">Page1DetailA</Link>
      <br />
      <Link to="/page1/detailB">Page1DetailB</Link>

      <Outlet />
    </div>
  );
};

結果

Page1リンク押下時

image.png

Page1DetailAリンク押下時

image.png

4. URLパラメータ

URLパラメータを扱うには、useParamsを利用します。
こちらはバージョン5と6で書き方に大きな違いはありません。

※ 以降のサンプルですが、ルート定義をrouter/Router.jsxへ分割していますのでご注意ください。

バージョン6の書き方

App.jsx
import { BrowserRouter, Link } from 'react-router-dom';

import './styles.css';
import { Router } from './router/Router';

export default function App() {
  return (
    <BrowserRouter>
      <div className="App">
        <Link to="/">Home</Link>
        <br />
        <Link to="/page1">Page1</Link>
        <br />
        <Link to="/page2">Page2</Link>
      </div>
      <Router />
    </BrowserRouter>
  );
}
router/Router.jsx
import { Routes, Route } from 'react-router-dom';

import { Home } from '../Home';
import { Page1 } from '../Page1';
import { Page2 } from '../Page2';
import { Page1DetailA } from '../Page1DetailA';
import { Page1DetailB } from '../Page1DetailB';
import { UrlParameter } from '../UrlParameter';

export const Router = () => {
  return (
    <Routes>
      <Route path="/" element={<Home />}></Route>
      <Route path="page1" element={<Page1 />}></Route>
      <Route path="page1/detailA" element={<Page1DetailA />}></Route>
      <Route path="page1/detailB" element={<Page1DetailB />}></Route>
      <Route path="page2" element={<Page2 />}></Route>
      <Route path="page2/:id" element={<UrlParameter />}></Route>
    </Routes>
  );
};
Page2.jsx
import { Link } from 'react-router-dom';

export const Page2 = () => {
  return (
    <div>
      <h1>Page2ページです</h1>
      <Link to="/page2/999">URL Parameter</Link>
    </div>
  );
};
UrlParameter.jsx
import { useParams } from 'react-router-dom';

export const UrlParameter = () => {
  const { id } = useParams();
  return (
    <div>
      <h1>UrlParameterページです</h1>
      <p>パラメータは {id} です</p>
    </div>
  );
};

5. クエリパラメータ

クエリパラメータを扱うには、useLocationを利用します。
こちらもバージョン5と6で書き方に大きな違いはありません。

バージョン6の書き方

Page2.jsx
import { Link } from 'react-router-dom';

export const Page2 = () => {
  return (
    <div>
      <h1>Page2ページです</h1>
      <Link to="/page2/999">URL Parameter</Link>
      <br />
      <Link to="/page2/999?name=hogehoge">Query Parameter</Link>
    </div>
  );
};
UrlParameter.jsx
import { useLocation, useParams } from 'react-router-dom';

export const UrlParameter = () => {
  const { id } = useParams();
  const { search } = useLocation();
  const query = new URLSearchParams(search);

  return (
    <div>
      <h1>UrlParameterページです</h1>
      <p>パラメータは {id} です</p>
      <p>クエリパラメータは {query.get('name')} です</p>
    </div>
  );
};

6. stateの受け渡し

注意
stateで値を遷移先に受け渡す場合のLink部分の書き方が、バージョン5とバージョン6で異なるため注意してください。

  • バージョン5:to属性にオブジェクトを指定し、pathnameキーに遷移先のパス、stateキーに受け渡したいstateの値をそれぞれ指定する
  • バージョン6:state属性に受け渡したいstateの値を指定する

The Link component in v6 accepts state as a separate prop instead of receiving it as part of the object passed to to so you'll need to update your Link components if they are using state:

The state value is still retrieved in the linked component using useLocation():

stateの値を受け取る際は、バージョン5同様useLocation()を使用します。

バージョン5の書き方

Page1.jsx
import { Link } from 'react-router-dom';

export const Page1 = () => {
  const arr = [...Array(100).keys()];

  return (
    <div>
      <h1>Page1ページです</h1>
      <Link to={{ pathname: '/page1/detailA', state: arr }}>Page1DetailA</Link> // to属性に指定
      <br />
      <Link to="/page1/detailB">Page1DetailB</Link>
    </div>
  );
};

バージョン6の書き方

Page1.jsx
import { Link } from 'react-router-dom';

export const Page1 = () => {
  const arr = [...Array(100).keys()];

  return (
    <div>
      <h1>Page1ページです</h1>
      <Link to="/page1/detailA" state={arr}> // state属性に指定
        Page1DetailA
      </Link>
      <br />
      <Link to="/page1/detailB">Page1DetailB</Link>
    </div>
  );
};

stateの受け取り(共通)

Page1DetailA.jsx
import { useLocation } from 'react-router-dom';

export const Page1DetailA = () => {
  const { state } = useLocation();
  console.log(state);

  return (
    <div>
      <h1>Page1DetailAページです</h1>
    </div>
  );
};

7. Linkを使わないページ遷移

注意
バージョン5ではuseHistoryを使用していましたが、バージョン6では以下のエラーになります。
バージョン6ではuseHistoryの代わりにuseNavigateが用意されています。
使用方法がuseHistoryとは微妙に異なるのでご注意ください。

React Router v6 introduces a new navigation API that is synonymous with <Link> and provides better compatibility with suspense-enabled apps.

エラー内容

Uncaught SyntaxError: The requested module '/node_modules/.vite/deps/react-router-dom.js?v=ef39f487' does not provide an export named 'useHistory'

image.png

バージョン5の書き方

Page1.jsx
import { Link, useHistory } from 'react-router-dom';

export const Page1 = () => {
  const arr = [...Array(100).keys()];
  const history = useHistory();

  const onClickDetailA = () => history.push('/page1/detailA');

  return (
    <div>
      <h1>Page1ページです</h1>
      <Link to="/page1/detailA" state={arr}>
        Page1DetailA
      </Link>
      <br />
      <Link to="/page1/detailB">Page1DetailB</Link>
      <br />
      <button onClick={onClickDetailA}>DetailA</button>
    </div>
  );
};
Page1DetailA.jsx
import { useLocation, useHistory } from 'react-router-dom';

export const Page1DetailA = () => {
  const { state } = useLocation();
  const history = useHistory();
  console.log(state);

  const onClickBack = () => history.goBack();

  return (
    <div>
      <h1>Page1DetailAページです</h1>
      <button onClick={onClickBack}>戻る</button>
    </div>
  );
};

バージョン6の書き方

Page1.jsx
import { Link, useNavigate } from 'react-router-dom';

export const Page1 = () => {
  const arr = [...Array(100).keys()];
  const navigate = useNavigate();

  const onClickDetailA = () => navigate('/page1/detailA');

  return (
    <div>
      <h1>Page1ページです</h1>
      <Link to="/page1/detailA" state={arr}>
        Page1DetailA
      </Link>
      <br />
      <Link to="/page1/detailB">Page1DetailB</Link>
      <br />
      <button onClick={onClickDetailA}>DetailA</button>
    </div>
  );
};
Page1DetailA.jsx
import { useLocation, useNavigate } from 'react-router-dom';

export const Page1DetailA = () => {
  const { state } = useLocation();
  const navigate = useNavigate();
  console.log(state);

  const onClickBack = () => navigate(-1); // ひとつ前の画面に戻る

  return (
    <div>
      <h1>Page1DetailAページです</h1>
      <button onClick={onClickBack}>戻る</button>
    </div>
  );
};

おわりに

個人的には、バージョン6の方がバージョン5よりもすっきり書けるようになっているなと感じました。

なお、今回バージョン5のソースコードをバージョン6で動くように修正するにあたり、公式サイトが大変役立ちました。

英語ではありますが、サンプルコード付きで解説されていますので、ぜひ参考にしてみてください!

参考

3
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?