【react-router-dom】4.xになって辛い。。理解したい人のためのreact-router-dom

【react-router@3.x→4.x】動かない。。アップデートで困っている人が読む記事(how to update react-router3.x vs 4.x)

【react-router@3.x→4.x】動かない。。アップデートで困っている人が読む記事(how to update react-router3.x vs 4.x)

最近react-routerを3から4に変更したのですが、色々チェンジがあって苦戦しました。
react-routerをあまり触る機会がなかった自分はとりあえずreact-router@1.0.0から2.0.0、3.0.0とあげていき、注意を払っていきましたが、3.0.0から4にあげた瞬間
大変なことになりました。

【react-router-dom】4.xになって辛い。。理解したい人のためのreact-router-dom
【react-router-dom】4.xになって辛い。。理解したい人のためのreact-router-dom

また関連してこのような記事を書きました
【react-router@3.x→4.x】動かない。。アップデートで困っている人が読む記事(props(history, location, match等)をRouteから渡したい!!)

出会ったエラー達

1.Uncaught TypeError: Cannot read property 'pathname' of undefined

2.Warning: Failed prop type: Invalid prop `history` of type `function` supplied to `Router`, expected `object`.

3.Uncaught Error: A <Router> may have only one child element

4.Uncaught Error: Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: undefined. You likely forgot to export your component from the file it's defined in. Check the render method of `App`.

5.Warning: You should not use <Route component> and <Route children> in the same route; <Route children> will be ignored

 

そこで、今担当しているサイトのrouter構造に似せた「最小のrouter周り」を実装して、一つ一つどこがどうエラー出しているのか探り、調べながら解決していきました。
その際記事があまりなくて困りましたので、同じ状況の方に
自分が変更した箇所をわかりやすく伝えようと思います。

参照リンク
公式
https://reacttraining.com/react-router/web/guides/quick-start

古いやつだけど理解するのに役立つ
https://github.com/reactjs/react-router-tutorial/tree/master/lessons

簡単なsample作ったり
Using Webpack and React-router to improve performance with lazy loading

この際理解を深める
Beginner’s Guide to React Router

トレーニングのdoc
https://github.com/ReactTraining/react-router/tree/5e69b23a369b7dbcb9afc6cdca9bf2dcf07ad432/docs/guides

react-router-domのdoc。apiかguideから。(これもなかなか見つからないとこに置いてある。。)
https://github.com/ReactTraining/react-router/tree/master/packages/react-router-dom/docs

実際の作業

react-router@3.x→4.xのエラー対応

1.Uncaught TypeError: Cannot read property 'pathname' of undefined

bundle.js:30282 Uncaught TypeError: Cannot read property 'pathname' of undefined
bundle.js:30282 Uncaught TypeError: Cannot read property 'pathname' of undefined

上記はpathnameが未定義と出ていますが、Uncaught TypeError: Cannot read property 'location' of undefinedと出る場合もあります。
historyから読み込んでいるBrowserRouterに原因があります。
下のエラーと同じ対応をしました。

2.Warning: Failed prop type: Invalid prop `history` of type `function` supplied to `Router`, expected `object`.

Warning: Failed prop type: Invalid prop `history` of type `function` supplied to `Router`, expected `object`.
Warning: Failed prop type: Invalid prop `history` of type `function` supplied to `Router`, expected `object`.

history関連。上の2つは下のように変更することで変わります
ここに書きました。

.Uncaught TypeError: Cannot read property 'pathname' of undefined
.Uncaught TypeError: Cannot read property 'pathname' of undefined

これを

.Uncaught TypeError: Cannot read property 'pathname' of undefined
.Uncaught TypeError: Cannot read property 'pathname' of undefined

ここを見ると
https://github.com/ReactTraining/react-router/blob/master/packages/react-router-dom/modules/BrowserRouter.js
react-router-dom内のBrowserRouterがhistoryを使っています。

npmのhistoryやreact-routerからcreateHistoryをimportして独自に実装するのをやめます。
4.xからは提供するBrowserRouterをreact-router-domからエイリアスとしてimportするようにすると消えます。

3.Uncaught Error: A <Router> may have only one child element

Uncaught Error: A <Router> may have only one child element
Uncaught Error: A may have only one child element

これは子供のコンポーネントをまとめて返しなさいということでした ここ を参照して とりあえずdivで包んでおいてください。

react-router-dom@4.x Uncaught Error: A may have only one child element
react-router-dom@4.x Uncaught Error: A may have only one child element

エラーが変わることを確認してください。

4.Uncaught Error: Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: undefined. You likely forgot to export your component from the file it's defined in. Check the render method of `App`.

bundle.js:8536 Uncaught Error: Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: undefined. You likely forgot to export your component from the file it's defined in. Check the render method of `App`.
Uncaught Error: Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: undefined. You likely forgot to export your component from the file it's defined in. Check the render method of `App`.

これが出たら試してもらいたいことがあります。 問題を切り分けました。
どこかのコンポーネントがexportされていないか、もしくはその呼び出し方に問題があるようです。 この場合最後の'App'と言われていますのでそこがうまいこと行っていません このように

Uncaught Error: Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: undefined. You likely forgot to export your component from the file it's defined in. Check the render method of `App`
Uncaught Error: Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: undefined. You likely forgot to export your component from the file it's defined in. Check the render method of `App`

中にあるやつごっそり消して(あとで「ctr-z」)みて適当に打った文字列が表示されて、エラーがなくなっていることを確認してください。 言われていることは

要素タイプが無効です:文字列(組み込みコンポーネントの場合)またはクラス/関数(複合コンポーネントの場合)が必要ですが、未定義です。 コンポーネントが定義されているファイルからコンポーネントをエクスポートするのを忘れた可能性があります。 `App`のレンダリングメソッドをチェックしてください。

なので独自に定義したコンポーネントとして渡す必要があります。(もしくは渡されてきていない) 該当のComponentが(上記の場合App)が正しくexportされているかもそうなのですが、 v4.0.0では使われていないComponentを呼び出している可能性があります。 それが該当のComponentからで、 レンダリングされているComponentもチェックしてみてください。 自分の場合 <IndexRouter component={Hoge}>を削除したらいけました。 これ、結構苦戦しました。。
IndexRouterの代わりにSwitchを実装します。
import { Switch } from 'react-router'
として
[javascript]
<Switch>
<Route exact path="/" component={Home}/>
<Route path='/about'component={About} />
<Route path='/feature' component={Features} />
<Route component={NoMatch}/>
</Switch>
[/javascript]
このようにします。
<Route exact path="/" component={Home}/>

remove <IndexRouter component={Hoge}>
use Switch instead of IndexRouter

5.Warning: You should not use <Route component> and <Route children> in the same route; <Route children> will be ignored

スクリーンショット 2017-04-09 15.55.27 Routeを同階層でネストすると言われます。 どういう時言われるかというと Router同士でネスとして {this.props.child}でレンダリング、親Routeから呼んでいる時です。 そこに書いてある通りネストされたRouteの子Routeは無視されます。 ここをみてください Routeでネストしています。 それを ここにある ようにネストした親の中から呼んでください。(これがベストプラクティスではないと思いますがWarningは消えます。環境に合わせてください) 3 以上ですが、 またエラーと出会ったらここのページ更新していきます。。 今回の変更箇所概要。

以上です。
もし周りでreact-router3.xから4.xに移行する人が困っていたらこのページ教えてあげてください。

追記 2017/4/13

historyからのpropsをRuteから渡したい方へ。こちら書きました。
【react-router@3.x→4.x】動かない。。アップデートで困っている人が読む記事(props(history, location, match等)をRouteから渡したい!!)

こちらのページは随時更新していきますのでブックマークお願いします!!
では〜〜