LoginSignup
27
20

More than 5 years have passed since last update.

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

Last updated at Posted at 2017-07-22

はじめに

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型を指定する

参考

27
20
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
27
20