13
10

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

react-routerでredirectするときに無限ループにならないようにする

Last updated at Posted at 2016-06-15

TL;DR;

  • react-routerのpathのマッチングで、URL末尾の"/"の扱いは…
    • <Route path="hoge"> → hogeにもhoge/にもマッチする
    • <Route path="hoge/"> → hoge/にのみマッチする
  • hogeへのアクセスをhoge/にリダイレクトしたくて <Redirect from="hoge" to="hoge/" />を記述すると、hoge/もRedirectのルーティングにマッチして無限リダイレクトが起きるが…
  • ルーティング定義の上から順に評価されるので、Redirect定義より上に、hoge/でマッチするルーティングを書くようにすると万事解決!

前提

react-router@2.4.1の話です。他のバージョンは調べてません。

背景

URL末尾の"/"(トレイリングスラッシュ)があったりなかったりは色々問題になります。(画像やCSSへの相対パスとか)

reactアプリケーションでもURLをRESTfulにしたいなと思うと、パスの途中に動的な要素が入ることがあります。
例: http://example.com/posts/12345/comments/
id=12345のpostのcomment一覧的な。

react-routerのLinkコンポーネント(aタグを生成)は相対パスをサポートしません。
Linkコンポーネントのみで画面遷移を作ろうとすると、動的部分も全て構築しなければなりません。

例えば

場合

Linkコンポーネントは絶対パスが必須なので、post_idをparamsで受け取っておいて、下記のようにする必要があります。

<Link to={`/posts/${this.props.params.post_id}/comments/new`}>新規コメント</Link>

Aタグならシンプルに<a href="./new">新規コメント</a>と書けますので、こちらを使いたくなります。
ただし、当然のことながら現在のURLが

アプリケーション内でパスを書き間違える(末尾"/"を忘れてしまう)こともありますし、利用しているフレームワーク等で末尾"/"の扱いが適当なこともあります。


※ Railsの認証ミドルウェアDeviseでは、仮に上記URLが要認証なパスだとして、

末尾"/"を取り除いてしまっているのはブラウザか?Deviseか?


そんなわけで、末尾"/"なしURLを末尾"/"ありURLにリダイレクトしたいです。

ハマりポイント

<Router history={history}>
  <Route path="/" component={App}>
    <Router path="posts">
      <IndexRoute component={PostIndex} />
      ...
      <Redirect from="comments" to="comments/" />  ← これでリダイレクトさせるつもり
      <Route path="comments/">
          <IndexRoute component={CommentIndex} />
          <Route path="new" component={NewComment} />
      </Route>
  </Route>
</Router>

この定義だと死にます。

pathのマッチングは、下記の法則があります。

  • <Route path="hoge"> → hogeにもhoge/にもマッチする
  • <Route path="hoge/"> → hoge/にのみマッチする

したがって、<Redirect from="comments" to="comments/" />という定義は、commentsをcomments/にリダイレクトしますが、comments/もcomments/にリダイレクトしようとして無限ループになります。

(Redirectコンポーネントのfrom属性はpath属性の別名なのでpathもfromも同じ挙動になります)

解決策

pathのマッチングは上から順に評価され、一番最初にマッチした物が適用される(正確にはもっと色々評価法則あるかもですが)ので、下記のように書くとOKです。

<Router history={history}>
  <Route path="/" component={App}>
    <Router path="posts">
      <IndexRoute component={PostIndex} />
      ...
      <Route path="comments/">    ← comments/にマッチする定義がRedirectより上にある
          <IndexRoute component={CommentIndex} />
          <Route path="new" component={NewComment} />
      </Route>
      <Redirect from="comments" to="comments/" />  
  </Route>
</Router>
13
10
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
13
10

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?