Webエンジニアの備忘録

およそ自己の作業メモとして残しております。

React+Redux環境でreact-router@v4への移行をおこなう

React Routerの4系がリリースされて久しいですが、相変わらず対応ができていませんでした。 ようやく重い腰を上げてアップデートしたのですが、噂に違わず障壁が多い移行だったため、メモがてら記録を残しておきます。

v3 → v4で何が変わったか

簡潔に言うと、とても良くなりました。

  • ルーティングコンポーネントのネストについての制約(縛り)が解消された。
  • contextを利用せずにパラメータを送れるようになった。

現在の状況

  • React+Reduxで構築されたサービスです。
  • react-router3系とreact-router-reduxを併せて利用していました。
  • 画面遷移はhistroyに対するrouter.push()がメインです。
<button onClick={context.router.push()} />

移行作業

パッケージの更新

今回更新したパッケージは以下の通りです。

react-router@3.0.0 → react-router-dom@4.2.2
  • DOMのみパッケージ分割されていましたが、こちらで事足りました。
react-router-redux@4.0.8 → @next
  • 4系への対応は@nextバージョンを利用するように、とのことでした。
  • それ以前のバージョンは2/3系用となっています。
query-string
  • クエリパラメータ取得に使います。

ルーティング部分

新たなルーティングがこちらです。

import React from 'react';
import { render } from 'react-dom';
import { HashRouter, Route, Switch } from 'react-router-dom';
import { createHashHistory } from 'history';
import { Provider } from 'react-redux';
import { ConnectedRouter } from 'react-router-redux';

  :

const history = createHashHistory();
const store = configureStore(history);

render(
  (
    <Provider store={store}>
      <HashRouter>
        <ConnectedRouter history={history}>
          <NaviComponent>
            <Switch>
              <Route exact path="/" component={Top} />
              <Route path="/contents/:id" component={Contents} />
              <Route path="/contents" component={Contents} />
            </Switch>
          </NaviComponent>
        </ConnectedRouter>
      </HashRouter>
    </Provider>
  ),
  document.getElementById('app'),
);
  • ConnectedRouterがreduxと連結したhistoryを注入してくれます。
  • Hashによるルーティングを利用しているのでHashRouterで括っています。
  • NaviComponentのように共通のコンポーネントはそのまま組み込めます。
  • Switchで括っておくと、マッチするRouteが複数あっても最初にマッチしたコンポーネントしか表示しません。
  • pathは"/contents(/:id)"と書けなくなってしまったため、2行に分けて記載しています。
    • もっと良い方法があればコメントなどください。
  • URLパラメータの:id引き渡しについても以下の通り変更がありました。
- props.params.id
+ props.match.params.id

アクションの置き換え

  • push / replace / go / goBack等のファンクションはcontext.router内から取得していましたが、withRouter()メソッドの登場によって、Route直下にない孫コンポーネントにもprops経由でこれを送ることができるようになりました。
import { withRouter } from 'react-router-dom';

  :

export default withRouter(Component);
  • また、相対パスが利用できるようになったことも注意が必要でした。
- this.context.router.push('contents');
+ this.props.history.push('/contents');

contextを廃止

Context - React

公式ドキュメントにもあるように、contextは非推奨仕様でした。そもそもReactRouterの実装のために用いていたので、これを機にすべて撤廃することにしました。

クエリパラメータの対処

  • クエリが元のsearchパラメータからしか取得できなくなっていました。
  • query-stringを利用することで、key/valueに分割して値を返してくれます。
const search = props.location.search;
// ='?key1=hoge&key2=fuga'

const params = perse(search);
// = {
//   key1: 'hoge',
//   key2: 'fuga',
// }
  • また、Switchからも見て取れるように、パスでのみコンポーネントが切り替わるためクエリの変化はpropsに流れてこなくなりました。
    • 上の対応として、props内のhistory.listen()メソッドを利用します。こちらはクエリを含め、historyの更新に対してコールバックをおこなってくれます。

所感

主に詰まったところしか記載していないのですが、大概の情報はこちらのページで仕入れる事ができました。 reacttraining.com

以下のサイトにもお世話になりました。 qiita.com

今回のredux連携のようなケースは単体でのパッケージ導入と異なり、公式ドキュメントだけだとなかなか追いづらいところもありましたが、開発者側も周辺パッケージを意識しているようで、今回のアップデートはかなりreduxに寄せたものだったとも思いました。

今後もナレッジについてはメモがてら残していこうと思います。

補足

1018/05/18訂正

サンプルのコードで、HashRouter と ConnectedRouter のネストが逆になっていました。今のコードは訂正後のコードになっています。

HashRouter から振ってきたものを history 反映しないとダメですよね… 手元で起きた不具合としては、「history が一巡遅れて」いました。

Reactビギナーズガイド ―コンポーネントベースのフロントエンド開発入門

Reactビギナーズガイド ―コンポーネントベースのフロントエンド開発入門