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
组件上指定“未知”属性时出错(即属性“标题”在<MyComp>
)。要同时指定两者,请提供 withRouter<IProps, React.Component<IProps>>((staticContext, ...props) => 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 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