React

React-Routerの基本

ReactReact

インストールする

react-routerはreactの機能ではないので(3d partyのライブラリ)、追加します。

yarn add react-router-dom

ルーティングの実装

import React from 'react';
import ReactDOM from 'react-dom';
import AppRouter from './routers/AppRouter';

ReactDOM.render(<AppRouter />, document.getElementById('app'));

AppRouter.jsでルーティング処理を書いていきます。

(ここではコンポーネントも含めてるけど通常は別ファイル)

全コード↓

import React from 'react';
import { BrowserRouter, Route, Switch, Link, NavLink } from 'react-router-dom';

const Header = () => (
  <div>
    <h1>My Site</h1>
    <NavLink to="/" activeClassName="is-active" exact={true}>Home</NavLink>
    <NavLink to="/about" activeClassName="is-active">About</NavLink>
    <NavLink to="/portfolio" activeClassName="is-active">Portfolio</NavLink>
  </div>
);

const Home = () => (
  <div>
    <h1>Home</h1>
    <p>This is my home page</p>
  </div>
);

const About = () => (
  <div>
    <h1>About</h1>
    <p>This is my about page</p>
  </div>
);

const Portfolio = () => (
  <div>
    <h1>Check my works</h1>
    <Link to="/portfolio/1">ItemOne</Link>
    <Link to="/portfolio/2">ItemTwo</Link>
  </div>
);

const PortfolioItem = (props) => (
  <div>
    <h1>PortfolioItem</h1>
    <p>The item with the id {props.match.params.id}</p>
  </div>
);

const NotFound = () => (
  <div>
    <h1>NotFound</h1>
    <p>404 error!</p>
    <Link to="/">Go home</Link> 
  </div>
);

const AppRouter = () => (
  <BrowserRouter>
    <div>
      <Header/>
      <Switch>
        <Route path="/" component={Home} exact={true}/>
        <Route path="/about" component={About} exact={true} />
        <Route path="/portfolio" component={Portfolio} exact={true} />
        <Route path="/portfolio/:id" component={PortfolioItem} />
        <Route component={NotFound}  />
      </Switch>
    </div>
  </BrowserRouter>
);

export default AppRouter;

解説

BrowserRouterとRouter

BrowserRouterのインスタンスを作り、子供にルーティングしたいコンポーネントを入れます。

Routeを使って、「このpath(URL)をユーザーが叩いたら、指定のコンポーネントをレンダーしてね」と伝える。

exact={true}と加えることで「pathが完全にマッチする時だけ」と指定できます。

  <BrowserRouter>
    <Route path="/" component={Home} exact={true}/> 
  </BrowserRouter>

※デフォルトはexact={false}なので他のcomponentもレンダーされる現象が起きる。例:AboutでHomeのコンポーネントもレンダーされるとか

エラー(404)ページ

<Route component={NotFound} /> では、pathは指定しないことで全URLが対象となります。

pathはオプションなので指定しなくても大丈夫)

ただこれだけだと上4つのRoutepathの時までレンダリングされてしまう。

既に他のRouteで指定しているURLは対象外として欲しいので、Switchを使う必要があります。

Switch上から順番にチェックしていきpathがマッチしたらルーティングチェックが終了します。

なので、エラー(404)ページを一番下に置いておくことで、上から全部チェックしてpathが存在しない場合のみNotFoundページがレンダリングされるという仕組み。

      <Switch>
        <Route path="/" component={Home} exact={true}/>
        <Route path="/about" component={About} exact={true} />
        <Route path="/portfolio" component={Portfolio} exact={true} />
        <Route path="/portfolio/:id" component={PortfolioItem} />
        <Route component={NotFound}  />
      </Switch>

<Link>はページをリフレッシュせずに高速に移動できます(<a>タグはサーバーサイド を経由する)

const NotFound = () => (
  <div>
    <h1>NotFound</h1>
    <p>404 error!</p>
    <Link to="/">Go home</Link> 
  </div>
);

NavLinkで動的スタイル

Navバーが必要なアプリやサイトの場合、NavLinkを使うと便利です。

該当のURLがレンダリングされる際、activeClassNameを使うことで、Navバー上で動的スタイルを反映できます。

(今このページを表示してる、というのがわかる感じ)

.is-active {
  font-weight: bold;
}

NavLinkもexact={true}を指定できる。

exact={true}については、将来的にはデフォルトになるとか言われてるぽいので、要期待ですね。

<NavLink to="/" activeClassName="is-active" exact={true}>Home</NavLink>

historyApiFallbackの設定

何も設定しないと、上記のpath(URL)が叩かれてもルーティングは機能しないです。

トップレベルのページindex.htmlはサーバーからもらうが、react-routerはクライアント側でルーティング処理をする為、このpathは存在しないとエラーになります。

なので、「サーバーを経由しないでクライアント側でルーティングの処理を面倒見るよ」と伝える必要があります。

  devServer: {
    contentBase: path.join(__dirname, 'public'), // publicフォルダーの場所を教える
    historyApiFallback: true //react-routerの設定
  }

historyApiFallbacktrueにするだけ。

尚、最初に一度だけindex.htmlがサーバーから返される仕組みについては別記事でまとめています。

動的なページの実装

RouteはいくつかObject形式でpropsを持っています。

  • history(ここのメソッドを使ってuserに表示するページを操作できるなど)
  • location(searchやhashなどがある)
  • match(paramsを使ってURLの一部をダイナミックにできるなど)

スラッシュ/配下の一部をダイナミックにしたい場合、Routepathを下記のように指定します。

(今回の場合は作品のidごと)

<Route path="portfolio/:id" component={Works} />

/idとすることで、portfolioItemページではidごとのページを動的にレンダリングできます。

matchのparamsにidが入っている
const Portfolio = () => (
  <div>
    <h1>Check my works</h1>
    <Link to="/portfolio/1">ItemOne</Link>
    <Link to="/portfolio/2">ItemTwo</Link>
  </div>
);

まとめ

大分簡潔ですが、基本的な内容を忘れないように…

React-Routerは手軽にルーティング処理が書けて非常に便利ですね。