まくろぐ

React の Router コンポーネントでページ切り替えを実現する

更新:
作成:

React の Router 系コンポーネント (BrowserRouter / HashRouter / MemoryRouter) を使用すると、React コンポーネントの表示切り替え、つまり、ページ切り替えを簡単に実現することができます。 例えば、/users というパスでアクセスしたときには Users コンポーネントを表示し、/projects というパスでアクセスしたときには Projects コンポーネントを表示する、といったことを実現できます。

react-router-dom のインストール

Router 系のコンポーネントは react-router-dom モジュールで提供されているので、まずはこれをインストールします。 TypeScript を使用する場合は、型定義ファイルも一緒にインストールしておきます。

$ npm install --save react-router-dom
$ npm install --save-dev @types/react-router-dom

BrowserRouter による表示切り替え

/p/nfxds8n/img-001.png
図: Router による表示切り替え

まず、表示切り替え対象となる Page1 / Page2 コンポーネントを用意しておきます。

components/page1.tsx
import * as React from 'react';

export const Page1: React.FC = () => {
  return <h1>Page1</h1>;
};
components/page2.tsx
import * as React from 'react';

export const Page2: React.FC = () => {
  return <h1>Page2</h1>;
};

次の Root コンポーネントでは、BrowserRouter コンポーネントを使って、上記の Page1Page2 コンポーネントの表示を切り替えます。 Router 系のコンポーネントには、Router というエイリアス名を付けるのが慣例なのでそれに従います。 Router コンポーネントの下には、表示切り替え用のリンク要素として Link コンポーネントを配置し、表示要素として Route コンポーネントを配置します。

components/root.tsx
import * as React from 'react';
import { BrowserRouter as Router, Link, Route } from 'react-router-dom';
import { Page1 } from './page1';
import { Page2 } from './page2';

export const Root: React.FC = () => {
  return (
    <Router>
      <nav>
        [<Link to="/page1">Page1</Link>]
        [<Link to="/page2">Page2</Link>]
      </nav>
      <Route path="/page1" component={Page1} />
      <Route path="/page2" component={Page2} />
    </Router>
  );
};

あとは、この Root コンポーネントを次のように表示します。

index.tsx
import * as React from 'react';
import * as ReactDOM from 'react-dom';
import { Root } from './components/root';

ReactDOM.render(<Root />, document.getElementById('root'));
☝️ Uncaught DOMException: Failed to execute 'pushState' on 'History' BrowserRouter を動作させるには、Web サーバー上の HTML ファイルをブラウザで開いている必要があります。 ローカルの HTML ファイルを使って動作させたい場合は、BrowserRouter の代わりに HashRouterMemoryRouter を使用すると動作します。
/p/nfxds8n/img-002.png
図: NavLink によるハイライト

Link コンポーネントの代わりに NavLink コンポーネントを使用すると、そのリンクがアクティブ(対象の Route が表示されているとき)の CSS スタイルを指定することができます。 スタイルを直接指定する場合は activeStyle 属性、CSS クラス名を指定する場合は activeClassName 属性を使用します。 なお、style 属性や className 属性で指定した CSS スタイルは、アクティブでないリンクにも反映されます。

components/root.tsx
import * as React from 'react';
import { HashRouter as Router, NavLink, Route } from 'react-router-dom';
import { Page1 } from './page1';
import { Page2 } from './page2';

// スタイル定義
const styles: {[key: string]: React.CSSProperties} = {
  link: {
    color: 'white',
    background: 'gray',
    marginRight: '0.2em',
    padding: '0.5em',
    textDecoration: 'none',
  },
  linkActive: {
    background: '#5ad',
  }
}

export const Root: React.FC = () => {
  return (
    <Router>
      <nav>
        <NavLink to="/page1" style={styles.link} activeStyle={styles.linkActive}>
          Page1
        </NavLink>
        <NavLink to="/page2" style={styles.link} activeStyle={styles.linkActive}>
          Page2
        </NavLink>
      </nav>
      <Route path="/page1" component={Page1} />
      <Route path="/page2" component={Page2} />
    </Router>
  );
};

リダイレクト (Redirect)

Redirect コンポーネントを使用すると、ユーザーが特定のパスにアクセスしたときに、別のパスにリダイレクトすることができます。 例えば、ルートパス (/) にアクセスしたときに、デフォルトページとして /page1 にリダイレクトする、といったことが可能です。

// import { HashRouter as Router, Redirect, Route } from 'react-router-dom';

<Router>
  <Redirect exact from="/" to="/page1" />
  <Route path="/page1" component={Page1} />
  <Route path="/page2" component={Page2} />
</Router>

これは、次のようにルートパス (/) にアクセスしたときに表示するコンポーネントを、/page1 にアクセスした場合と同じにするのと似ていますが、同一の内容を表示するのであれば、リダイレクトを使うことをおすすめします。

<Router>
  <Route exact path="/" component={Page1} />
  <Route path="/page1" component={Page1} />
  <Route path="/page2" component={Page2} />
</Router>

なぜなら、React 内部のパス情報が変更されないと、NavLink 要素の activeClassNameactiveStyle 属性が動作しないなどの問題が出るからです。

関連記事

まくろぐ
サイトマップまくへのメッセージ