【问题标题】:react-router-dom with TypeScript使用 TypeScript 的 react-router-dom
【发布时间】:2017-10-22 10:01:54
【问题描述】:

我正在尝试将 react 路由器与 TypeScript 一起使用。但是,我在使用 withRouter 函数时遇到了一些问题。在最后一行,我遇到了非常奇怪的错误:

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 '{}’

代码如下:

import * as React from 'react';
import { connect } from 'react-redux';
import { RouteComponentProps, withRouter } from 'react-router-dom';

interface HomeProps extends RouteComponentProps<any> {
}

interface HomeState { }

class Home extends React.Component<HomeProps, HomeState> {
  constructor(props: HomeProps) {
    super(props);
  }
  public render(): JSX.Element {
    return (<span>Home</span>);
  }
}

const connectModule = connect(
  (state) => ({
    // Map state to props
  }),
  {
    // Map dispatch to props
  })(Home);

export default withRouter(connectModule);

【问题讨论】:

    标签: reactjs typescript react-router react-router-v4 react-router-redux


    【解决方案1】:

    我是怎么做的:

    interface IOwnProps {
      style?: CSSProperties;
    }
    
    interface INavProps {
      item?: string;
    }
    
    type IProps = IOwnProps & RouteComponentProps<INavProps>;
    
    export default class MapScreen extends PureComponent<IProps> {
      ...
    }
    

    【讨论】:

      【解决方案2】:

      我使用不同的方法来解决这个问题。我总是将不同的属性(路由器、常规和调度)分开,所以我为我的组件定义了以下接口:

      interface HomeRouterProps {
        title: string;   // This one is coming from the router
      }
      
      interface HomeProps extends RouteComponentProps<HomeRouterProps> {
        // Add your regular properties here
      }
      
      interface HomeDispatchProps {
        // Add your dispatcher properties here
      }
      

      您现在可以创建一个新类型,将所有属性组合在一个类型中,但我总是在组件定义期间组合类型(我不在这里添加状态,但如果您需要,请继续)。组件定义如下所示:

      class Home extends React.Component<HomeProps & HomeDispatchProps> {
        constructor(props: HomeProps & HomeDispatchProps) {
          super(props);
        }
      
        public render() {
          return (<span>{this.props.match.params.title}</span>);
        }
      }
      

      现在我们需要通过容器将组件连接到状态。它看起来像这样:

      function mapStateToProps(state, ownProps: HomeProps): HomeProps => {
        // Map state to props (add the properties after the spread)
        return { ...ownProps };
      }
      
      function mapDispatchToProps(dispatch): HomeDispatchProps {
        // Map dispatch to props
        return {};
      }
      
      export default connect(mapStateToProps, mapDispatchToProps)(Hello);
      

      此方法允许完全类型化的连接,因此组件和容器是完全类型化的,并且可以安全地对其进行重构。唯一对重构不安全的是路由中映射到HomeRouterProps 接口的参数。

      【讨论】:

      • 你在哪里给withRouter打电话?
      • 赞成。这确实是解决问题的“正确”方法。如果您使用的是 TypeScript,请正确输入您的组件!
      • 现在,这大约有一年了,但 HelloDispatchProps 不应该是 HomeDispatchProps 吗?
      • mapStateToProps 中,不应该是ownProps 类型的RouteComponentProps&lt;HomeRouterProps&gt;
      【解决方案3】:

      浏览 typescript 定义后,我发现了 RouteComponentProps 接口,所以我现在像这样对容器进行建模

      type RouteParams = {
          teamId: string; // must be type string since route params
      }
      
      interface Props extends RouteComponentProps<RouteParams>, React.Props<RouteParams> { }
      
      type State = {
          players: Array<Player>;
      }
      
      export class PlayersContainer extends React.Component<Props, State>{} 
      

      现在在组件类中,可以像这样访问路由道具: let teamid = this.props.match.params.teamId;

      【讨论】:

        【解决方案4】:

        +1 表示 Ramon 的回答。您绝对可以在这里获得完整的类型覆盖。补充一点 - 我添加了对 withRouter 的调用。

        interface FooProps extends RouteComponentProps<Foo> {
          foo: string;
        }
        
        const Foo = ({ history, foo }: FooProps) => <span/>;
        
        const RoutedFoo = withRouter(Foo);
        

        我的依赖:

        "@types/react-router-dom": "^4.3.0",
        "typescript": "^2.9.2",
        

        【讨论】:

          【解决方案5】:

          这是一个打字稿打字问题。由于 withRouter() 将在运行时提供你需要的路由道具,你想告诉消费者他们只需要指定 other 道具。在这种情况下,您可以只使用普通的 ComponentClass:

          export default withRouter(connectModule) as React.ComponentClass<{}>;
          

          或者,如果您想要传入其他道具(在名为 OwnProps 的接口中定义),您可以这样做:

          export default withRouter(connectModule) as React.ComponentClass<OwnProps>;
          

          讨论有点多here

          【讨论】:

          • withRouter 使用泛型,因此您可以键入而不是类型转换:export default withRouter&lt;OwnProps&gt;(connectModule);
          【解决方案6】:

          我认为这是打字稿打字编译问题,但我找到了解决方法:

          interface HomeProps extends RouteComponentProps<any>, React.Props<any> {
          }
          

          【讨论】:

          • 我更愿意将此标记为预期行为。 Kevin Welcher 在这里概述了为什么需要更多:medium.com/@kaw2k/a-letter-of-appreciation-253ecab3f7d2
          • 在类型上声明 any 会首先消除使用类型的目的。方法错误。
          • 是的,这不是正确的方法,这就是我将其归类为解决方法的原因。
          【解决方案7】:

          看起来你有正确的用法将匹配、历史和位置道具应用到你的组件。我会检查你的 node_modules 目录,看看你有哪些版本的 react-routerreact-router-dom,以及 @types 模块。

          我的代码与你基本相同,我的代码使用以下版本:

          {
            "@types/react-router-dom": "^4.0.4",
            "react-router-dom": "^4.1.1",
          }
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2017-12-05
            • 2018-02-12
            • 2021-05-10
            • 2021-03-30
            • 2023-01-12
            • 1970-01-01
            • 1970-01-01
            • 2019-11-03
            相关资源
            最近更新 更多