reactjs React Router - 更新版本后 withRouter 上的 Typescript 错误

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/48219432/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-06 04:22:20  来源:igfitidea点击:

React Router - Typescript errors on withRouter after updating version

reactjstypescriptreact-router

提问by 29er

I just tried to upgrade my React app to

我只是尝试将我的 React 应用程序升级到

react-router - 4.0.19 to 4.0.20

反应路由器 - 4.0.19 到 4.0.20

react- 16.0.30 to 16.0.34

反应- 16.0.30 到 16.0.34

typescript- version "2.7.0-insiders.20180108"

打字稿-版本“2.7.0-insiders.20180108”

In my app, wherever I am using 'withRouter', I now get cryptic Typescript errors. I even replaced all interface props with 'any' just to try to make it work.

在我的应用程序中,无论我在哪里使用“withRouter”,我现在都会遇到神秘的 Typescript 错误。我什至用“any”替换了所有界面道具,只是为了让它工作。

import * as React from 'react';
import { Switch, Route, withRouter} from 'react-router-dom';
import { Login } from './Login';
import { connect } from 'react-redux';
import { RootAction, RootState } from './_redux';

class MainForm extends React.Component<any> {

  constructor(props: any) {
    super(props);
  }

  render() {

    return (
      <Switch>
        <Route exact={true} path="/" component={Login}/>
        <Route  path="/accounts" component={AccountsView}/>
      </Switch> 
    );
  }
}

const mapStateToProps = (state: RootState) => ({
  state
});

export const Main = withRouter(connect(mapStateToProps)(MainForm);

error TS2345: Argument of type 'ComponentClass> & { WrappedComponent: ComponentType; }' is not assignable to parameter of type 'ComponentType>'. Type 'ComponentClass> & { WrappedComponent: ComponentType; }' is not assignable to type 'StatelessComponent>'. Type 'ComponentClass> & { WrappedComponent: ComponentType; }' provides no match for the signature '(props: RouteComponentProps & { children?: ReactNode; }, context?: any): ReactElement | null'.

错误 TS2345:'ComponentClass> 类型的参数 & { WrappedComponent: ComponentType; }' 不可分配给类型为 'ComponentType>' 的参数。类型 'ComponentClass> & { WrappedComponent: ComponentType; }' 不可分配给类型 'StatelessComponent>'。类型 'ComponentClass> & { WrappedComponent: ComponentType; }' 不匹配签名 '(props: RouteComponentProps & { children?: ReactNode; }, context?: any): ReactElement | 空值'。

If i convert the last line to this :

如果我将最后一行转换为:

export const Main = connect(mapStateToProps)(MainForm);

I don't get errors. seriously frustrated here. Thanks

我没有错误。在这里严重受挫。谢谢

EDIT, I changed to

编辑,我改为

export const Main = connect(mapStateToProps)(withRouter(MainForm));

like suggested by Mayank Shukla. but now get the error:

就像 Mayank Shukla 建议的那样。但现在得到错误:

error TS2345: Argument of type 'ComponentClass>' is not assignable to parameter of type 'ComponentType<{ state: RootState; } & DispatchProp>'. Type 'ComponentClass>' is not assignable to type 'StatelessComponent<{ state: RootState; } & DispatchProp>'. Type 'ComponentClass>' provides no match for the signature '(props: { state: RootState; } & DispatchProp & { children?: ReactNode; }, context?: any): ReactElement | null'.

错误 TS2345:“ComponentClass>”类型的参数不可分配给“ComponentType<{ state: RootState;”类型的参数。} & DispatchProp>'。类型 'ComponentClass>' 不能分配给类型 'StatelessComponent<{ state: RootState; } & DispatchProp>'。类型 'ComponentClass>' 不匹配签名 '(props: { state: RootState; } & DispatchProp & { children?: ReactNode; }, context?: any): ReactElement | 空值'。

回答by Pavel

I just upgraded to TypeScript 2.6 and got same issue.

我刚刚升级到 TypeScript 2.6 并遇到了同样的问题。

I managed to resolve it by using RouteComponentProps.

我设法通过使用来解决它RouteComponentProps

For URL http://localhost:8080/your-component/abcand route

对于 URLhttp://localhost:8080/your-component/abc和路由

<Route component={YourComponent} path="/your-component/:param1?" />

Component should look like this:

组件应如下所示:

import * as React from 'react'
import { withRouter } from 'react-router-dom';
import {RouteComponentProps} from "react-router";

// Type whatever you expect in 'this.props.match.params.*'
type PathParamsType = {
    param1: string,
}

// Your component own properties
type PropsType = RouteComponentProps<PathParamsType> & {
    someString: string,
}

class YourComponent extends React.Component<PropsType> {
    render() {

        console.log(this.props); // Prints all props including routing-related
        console.log(this.props.match.params.param1); // Prints 'abc'
        console.log(typeof this.props.match.params.param1 === 'string'); // prints 'true'

        return <div>...</div>;
    }
}

export default withRouter(YourComponent);

回答by jakobdo

I have to solve it like this:

我必须像这样解决它:

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

interface IProps extends RouteComponentProps<any> {
  title: string;
}

class MyComp extends React.Component<IProps> {
    public render(){
        return (
           <h1>{this.props.title}</h1>
        )
    }
}

export default withRouter<IProps>(MyComp);

回答by Max

Here is how I usually strucutre my typed React components:

以下是我通常如何构建我的类型化 React 组件:

// These props are provided when creating the component
interface OwnProps {
    // ...
}

// These props are provided via connecting the component to the store
interface StateProps {
    // ...
}

// These props are provided by the router
interface PathProps {
    // ...
}

class Component extends React.Component<OwnProps & StateProps & RouteComponentProps<PathProps>> {
    // ...
}

const mapStateToProps = (state: State, props: OwnProps): StateProps => ({
    // ...
});

export default withRouter(
    connect(mapStateToProps)(Component)
);

回答by Daniel Krom

Another solution, using decorators

另一种解决方案,使用装饰器

import { withRouter, RouteComponentProps } from "react-router";

// inform we match url /:id
interface IMatchParams {
    id: string;
}

// Note we use Partial<RouteComponentProps> to make all RouteComponentProps as optional for high order component
interface IComponentProps extends Partial<RouteComponentProps<IMatchParams>> {
    myPersonalProp: string;
}

@withRouter
export default class MyClass extends React.Component<IComponentProps>{

    public componentDidMount(){
        console.log(this.props.match.params.id);
    }
}

回答by Hymankobec

Working syntax variant for Type Script application is:

Type Script 应用程序的工作语法变体是:

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

    interface ComponentProps {
    // Your properties here
    }

    interface ComponentState {
    // Your properties here
    }

    interface MapStateToPropsTypes {
    // Your properties here
    }

    interface MapDispatchToPropsTypes {
    // Your properties here
    }

    class MyComponentName extends React.Component<ComponentProps, ComponentState> {
        constructor(props: ComponentProps) {
            super(props);
        }
    }

    export default withRouter(
    connect<MapStateToPropsTypes, MapDispatchToPropsTypes>(
        mapStateToProps,
        mapDispatchToProps
      )(MyComponentName) as any
    );

回答by oliyoung

Here's a functional react approach I use

这是我使用的功能反应方法

import { RouteComponentProps } from "react-router";

interface Props extends RouteComponentProps {
    thing: Thing | false;
    onAction?: () => void;
}

export default withRouter(({ thing, onAction, history }: Props) => {

回答by Raunhofer

I was struggling with a very similar/same issue with Typescript 3.6 and couldn't find a solution online so I'll share my own solution here. I hope it helps someone working with a more complex app.

我正在努力解决与 Typescript 3.6 非常相似/相同的问题,并且无法在线找到解决方案,因此我将在这里分享我自己的解决方案。我希望它可以帮助那些使用更复杂的应用程序的人。

import React, { memo } from 'react';
import { withRouter, RouteComponentProps } from 'react-router-dom';
import { ThunkDispatch } from 'redux-thunk';
import { connect } from 'react-redux';
import { AnyAction } from 'redux';

interface IStateProps {
  name: string;
  sessionLanguage: string;
}

interface IDispatchProps {
  handleLogout: () => void;
}

type Props = IStateProps & IDispatchProps & RouteComponentProps<any>;

const MyCoolComponent = ({
  sessionLanguage,
  handleLogout,
  history,
}: Props) => {
  return null;
};

const mapStateToProps = (state: IAppState): IStateProps => ({
  name: state.getIn(['session', 'name']),
  sessionLanguage: state.getIn(['session', 'language']),
});

const mapDispatchToProps = (
  dispatch: ThunkDispatch<{}, {}, AnyAction>
): IDispatchProps => ({
  handleLogout: async () => {
    await dispatch(logout());
  },
});

export default withRouter(
  connect<IStateProps, IDispatchProps, {}, IAppState>(
    mapStateToProps,
    mapDispatchToProps
  )(memo(NavigationLayout))
);

Some notes:

一些注意事项:

  • Important parts are the interfaces, RouteComponentProps, type Props, React component typing and the export default withRouter(...). mapStateToProps and mapDispatchToProps are just examples.
  • IAppState defines my app's redux store's typings. If you don't have it.
  • I'm using immutable redux store here (that's why "state.getIn...").
  • 重要的部分是接口、RouteComponentProps、类型 Props、React 组件类型和导出默认值 withRouter(...)。mapStateToProps 和 mapDispatchToProps 只是示例。
  • IAppState 定义了我的应用程序的 redux 商店的类型。如果你没有它。
  • 我在这里使用不可变的 redux 存储(这就是“state.getIn...”的原因)。