Help us understand the problem. What is going on with this article?

Redux + React Router + TypeScript環境 connectで生成したHOCでLink遷移ができない

More than 3 years have passed since last update.

はじめに

Redux + React Router + TypeScriptの環境を構築していたところ
connectで生成されたコンポーネント内でReact RouterのLinkでうまく遷移ができななかったので、調べた解決方法を載せておきます。

環境

開発要素 バージョン
TypeScript 2.4.1
react 15.6.1
react-router-dom 4.1.1
redux 3.7.2
react-redux 5.0.5

問題

React RouterのLinkコンポーネントを使用したリンクをクリックしても遷移しない(renderメソッドが実行されない)。

その他調べて分かったこと
  • reduxなし(Providerやconnectなし)の場合はリンクで遷移する
  • Linkをクリック時は遷移しないが、そのあとに dispatch呼び出す動作をすると 遷移する

ソース

問題が発生した時のソースコード(一部)

Root.tsx
import * as React from "react"
import { BrowserRouter } from "react-router-dom"
import { Provider } from "react-redux"
import App from "./App"

const store = // ... ストア生成

class Root extends React.Component<{}, {}> {
  public render() {
    return (
      <Provider store={store}>
        <BrowserRouter>
          <App />
        </BrowserRouter>
      </Provider >
    )
  }
}

export default Root
App.tsx
import * as React from "react"
import { connect } from "react-redux"
import { Switch, Route, Link } from "react-router-dom"

interface Props extends React.Props<{}> {
  /* ... */
}

class App extends React.Component<Props, {}> {
  public render() {
    return (
      <Switch>
        // <Link />コンポーネントはChildA, ChildBの中で指定
        <Route exact={true} path="/" component={ChildA} />
        <Route path="/page2" component={ChildB} />
      </Switch>
    )
  }
}

/* RootStateはアプリのStateの型 */
const mapStateToProps = (rootState: RootState, onwProps: Props) => ({
  /* ... */
})

const mapPropsToDispatch = {
 /* ... */
}

export default connect(mapStateToProps, mapPropsToDispatch)(App)

RootコンポーネントではProvider, BrowserRootでAppコンポーネント(アプリケーションのメインコンポーネント) ラップして呼び出している。

対応1

React Routerドキュメントによると、connectで生成されたコンポーネントの場合、Linkで遷移ができないことが記載されていた。
これの対応方法としてwithRouterメソッドでラップしなければならいよう。

--- import { Switch, Route, Link } from "react-router-dom"
+++ import { Switch, Route, Link, withRouter } from 'react-router-dom

--- export default connect(mapStateToProps, mapPropsToDispatch)(App)
+++ export default withRouter(connect(mapStateToProps, mapPropsToDispatch)(App))

別の問題が発生

上記のようにコードを修正したところ、今度はTypeScriptのビルドエラーが発生。

ERROR in [at-loader] ./src/pages/App.tsx:57:31
    TS2345: Argument of type 'ComponentClass<{}>' is not assignable to parameter of type 'StatelessComponent<RouteComponentProps<any>> | ComponentClass<RouteComponentProps<any>>'.
  Type 'ComponentClass<{}>' is not assignable to type 'ComponentClass<RouteComponentProps<any>>'.
    Type '{}' is not assignable to type 'RouteComponentProps<any>'.
      Property 'match' is missing in type '{}'.

対応2

withRouterの引数はコンポーネントのPropsにRouteComponentPropsを指定したコンポーネントである必要がある。
そこで、AppコンポーネントのPropsにRouteComponentPropsを継承した型を使用するよう修正した。

--- import { withRouter } from 'react-router-dom'
+++ import { withRouter, RouteComponentProps } from 'react-router-dom'

--- interface Props extends React.Props<{}> {
+++ interface Props extends RouteComponentProps<{}>, React.Props<{}> {

--- export default withRouter(connect(mapStateToProps, mapPropsToDispatch)(App))
+++ export default withRouter<{}>(connect(mapStateToProps, mapPropsToDispatch)(App))

以上で、Linkでの遷移ができるようになった。

まとめ

  • コンポーネントのPropsにRouteComponentPropsのプロパティが必要
  • connectで生成されるコンポーネントの型に元コンポーネントのPropsを引き継ぐにはmapStateToPropsの第二引数に元コンポーネントのProps型を指定する

参考

r-tamura
最近はWeb技術全般に興味があります。
https://rtam.xyz/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away