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

Posted

技术标签:

【中文标题】React Router - 更新版本后 withRouter 上的 Typescript 错误【英文标题】:React Router - Typescript errors on withRouter after updating version 【发布时间】:2018-06-21 12:18:31 【问题描述】:

我刚刚尝试将我的 React 应用升级到

react-router - 4.0.19 到 4.0.20

react- 16.0.30 到 16.0.34

typescript-版本“2.7.0-insiders.20180108”

在我的应用程序中,无论我在哪里使用“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);

错误 TS2345: 'ComponentClass> & WrappedComponent:组件类型; ' 不可分配给 'ComponentType>' 类型的参数。类型 'ComponentClass> & WrappedComponent: 组件类型; ' 不可分配给类型 '无状态组件>'。 类型 'ComponentClass> & WrappedComponent: ComponentType; ' 不提供与签名 '(props: RouteComponentProps & children?: ReactNode; ,上下文?:任何): 反应元素 |空'。

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

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

我没有收到错误。在这里严重沮丧。 谢谢

EDIT,我改成

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

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

错误 TS2345:'ComponentClass>' 类型的参数是 不可分配给“ComponentType'。类型 'ComponentClass>' 是 不可分配给类型 'StatelessComponent'。 类型 'ComponentClass>' 不提供与签名 '(props: state: RootState; & DispatchProp & 孩子?:反应节点; ,上下文?:任何):ReactElement |空'。

【问题讨论】:

试试这个:connect(mapStateToProps)(withRouter(MainForm)) 感谢@MayankShukla。我认为这是一个进步,但现在我得到了错误,如上所述。 我猜你的语法不正确,withRouter(connect(mapStateToProps)(MainForm)),你需要一个额外的结束括号 我解决了这个问题,但仍然遇到同样的错误:( 【参考方案1】:

对我来说只有两个键是:

    正确输入道具
interface MyComponentProps extends RouteComponentProps /*...*/
class MyComponent extends React.Component<MyComponentProps , MyComponentState> /*...*/
    withRouter() 包装 connect()
withRouter(
  connect(null, 
    ...MyReduxActions
  )(MyComponent)
);

【讨论】:

【参考方案2】:

为了仍然允许在您的组件上使用自定义参数,您必须将您的 props 接口提供给withRouter

此外,withRouter 要求您指定正在使用的组件类型(即 FunctionComponent / Component)。

还要注意withRouter 将提供staticContext 以及道具。这应该从集合中删除。 props 在将它们传递给包装的组件之前,否则,您将收到此错误(除非您专门接口您的组件以接受 staticContext)。

index.js:1 Warning: React does not recognize the 'staticContext' prop on a DOM element...

对于函数组件,以下是如何正确键入 withRouter 包装器的示例:

对于类组件,这里是一个如何正确键入 withRouter 包装器的示例。

import React,  FunctionComponent  from 'react';
import  RouteComponentProps, withRouter  from 'react-router-dom';

interface MyCompProps extends RouteComponentProps<any> 
  title: string;


const MyComp: FunctionComponent<MyCompProps> = ( title ) => (
    <h1> title </h1>
);

export default withRouter<MyCompProps, Component<MyCompProps>>(( staticContext, ...props ) => MyComp(props));

对于类组件,这里是一个如何正确键入 withRouter 包装器的示例。

import React,  Component  from 'react';
import  RouteComponentProps, withRouter  from 'react-router-dom';

interface MyCompProps extends RouteComponentProps<any> 
  title: string;


class MyComp extends Component<MyCompProps> 
    public render()
        return (
           <h1>this.props.title</h1>
        )
    


export default withRouter<MyCompProps, Component<MyCompProps>>(( staticContext, ...props ) => MyComp(props));

【讨论】:

【参考方案3】:

如果类型“any”有问题,您可以这样做。它对我有用。

import  withRouter, RouteComponentProps  from 'react-router-dom';

type ComponentProps = RouteComponentProps;

const Component: React.FC = () => 
   return <div>This is component</div>


export default withRouter(Component)

【讨论】:

【参考方案4】:

我遇到了这个问题,最接近我的问题的答案是这个线程。但是,我不得不稍微更改以下建议。分享如果以防万一帮助其他人...


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

export interface MyComponentProps extends RouteComponentProps<any> 
  propA: String;
  propB: Number;


function MyComponent(props: MyComponentProps) 
   return (
     <div>
        <div>props.propA - props.propB</div>
        <button onClick=() => props.history.push('/some-other-page')>Go</button>
     </div>
   )


export default withRouter(MyComponent);


【讨论】:

【参考方案5】:

我在 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))
);

一些注意事项:

重要的部分是接口、RouteComponentProps、type Props、React 组件类型和导出默认 withRouter(...)。 mapStateToProps 和 mapDispatchToProps 只是示例。 IAppState 定义了我的应用程序的 redux 商店的类型。如果你没有。 我在这里使用不可变的 redux 存储(这就是为什么“state.getIn...”)。

【讨论】:

【参考方案6】:

这是我使用的一种函数式反应方法

import  RouteComponentProps  from "react-router";

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


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

【讨论】:

【参考方案7】:

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
    );

【讨论】:

“我喜欢在我的项目中使用 TypeScript,但我给所有的东西都设置了 any 类型并且它可以工作!!” 没错。但这只是关于组件连接语法的示例,而不是关于打字稿。【参考方案8】:

我必须这样解决:

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);

【讨论】:

@jakbdo 我收到一个错误 --- 预期有 2 个类型的参数,但在 withRouter 上得到了 1 个。 @DeltaTango 在升级到 5.x 后遇到了同样的问题。我通过完全删除类型参数来解决它,这突然起作用了:withRouter(MyComp)。由于之前的升级,我需要添加 type 参数,但是从 5.x 开始,它似乎可以通过省略它来工作。 "react-router": "5.2.0""@types/react-router": "5.1.8" @MarcelKirsche 从withRouter 调用中删除类型声明将导致在MyComp 组件上指定“未知”属性时出错(即属性“标题”在&lt;MyComp&gt;)。要同时指定两者,请提供 withRouter&lt;IProps, React.Component&lt;IProps&gt;&gt;((staticContext, ...props) =&gt; MyComp(props));【参考方案9】:

这是我通常如何构建我的类型化 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)
);

【讨论】:

【参考方案10】:

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

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);
    

【讨论】:

谢谢丹尼尔。好东西,你介意我问一下你需要在构建中引入哪些其他库来支持装饰器吗?我不久前尝试过,只是遇到了太多的 TS 错误......所以放弃了 @29er 第一步,您需要在tsconfig.json 下的compilerOptions 下添加标志experimentalDecorators: true。如果仍然有问题,请将编译target 更改为 es5,如果目标是 es7,则不会转译装饰器,并且大多数浏览器\node 版本尚不支持。【参考方案11】:

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

我设法通过使用RouteComponentProps 解决了它。

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

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

组件应如下所示:

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);

【讨论】:

很高兴知道!实际上,我只是在应用程序中完全删除了 withRouther 并使用了 BrowserRouter。从 'react-router-dom' 导入 BrowserRouter as Router, Route, RouteComponentProps ;出于某种原因,这样做让我摆脱了它并仍然传递路由道具。 我在 App.tsx 中使用了 withRouter。

以上是关于React Router - 更新版本后 withRouter 上的 Typescript 错误的主要内容,如果未能解决你的问题,请参考以下文章

react-router@4.0 使用和源码解析

React Router 自动更改后如何更新状态?

[React Router V4] Create Basic Routes with the React Router v4 BrowserRouter

每次更改页面时都会调用 React useEffect (with []) (React Router)

[Preact] Integrate react-router with Preact

React Routing with Express, Webpack dev middleware, React router dom