如何将 TypeScript 与 withRouter、connect、React.Component 和自定义属性一起使用?
Posted
技术标签:
【中文标题】如何将 TypeScript 与 withRouter、connect、React.Component 和自定义属性一起使用?【英文标题】:How to use TypeScript with withRouter, connect, React.Component and custom properties? 【发布时间】:2018-08-07 09:31:33 【问题描述】:我有一个使用 connect
、withRouter
并接收自定义属性的 React Component
。我正在尝试将其转换为 TypeScript,我想知道我是否正确执行此操作。至少,我现在没有错误。
这是显示概念的代码:
import * as React from 'react'
import connect from 'react-redux';
import withRouter, RouteComponentProps from 'react-router';
import
fetchTestLists,
newTestList,
displayTestList,
from '../../../actions/index';
interface StateProps
testList: any; // todo: use the type of state.myList to have validation on it
interface DispatchProps
fetchTestLists: () => void;
newTestList: () => void;
displayTestList: (any) => void; // todo: replace any with the actual type
interface Props // custom properties passed to component
className: string;
type PropsType = StateProps & DispatchProps & Props;
class MyTest extends React.Component<PropsType & RouteComponentProps<>, >
constructor(props)
super(props);
this.handleCellClick = this.handleCellClick.bind(this);
this.newTestList = this.newTestList.bind(this);
componentDidMount()
this.props.fetchTestLists();
handleCellClick(row, column, event)
this.props.displayTestList(row);
newTestList(e)
this.props.newTestList()
render()
return (
<div className=this.props.className>
</div>
);
const mapStateToProps = (state): StateProps => (
testList: state.myList, // todo: define a type for the root state to have validation here
);
const dispatchToProps =
fetchTestLists,
newTestList,
displayTestList,
;
export default withRouter<Props & RouteComponentProps<>>(connect<StateProps, DispatchProps>(
mapStateToProps,
dispatchToProps,
)(MyTest) as any);
组件是这样使用的:<MyTest className="active" />
我必须做很多实验才能让它发挥作用。例如:
1) 当我像这样省略 withRouter 的类型时:
export default withRouter(connect...
然后我得到TS2339: Property 'className' does not exist on type 'IntrinsicAttributes & IntrinsicClassAttributes<Component<Pick<RouteComponentProps<any>, never>, C...'.
不知何故,这里有人建议:React router in TypeScript- both router and own props,尽管我不了解这个概念。
2) 如果您想知道最后一行 as any
,这与 https://github.com/DefinitelyTyped/DefinitelyTyped/issues/18999 有关,没有它我会收到此错误:
TS2345: Argument of type 'ComponentClass<Pick<any, never>> & WrappedComponent: ComponentType<any>; ' is not assignable to parameter of type 'ComponentType<Props & RouteComponentProps<>>'.
Type 'ComponentClass<Pick<any, never>> & WrappedComponent: ComponentType<any>; ' is not assignable to type 'StatelessComponent<Props & RouteComponentProps<>>'.
Type 'ComponentClass<Pick<any, never>> & WrappedComponent: ComponentType<any>; ' provides no match for the signature '(props: Props & RouteComponentProps<> & children?: ReactNode; , context?: any): ReactElement<any> | null'.
那么这是正确的做法吗?你在哪里看到问题?我基本上使用的是所有最新版本,这是我的 package.json 中的一个 sn-p:
"react": "^16.2.0",
"redux": "^3.7.2",
"react-dom": "^16.2.0",
"react-redux": "^5.0.6",
"react-router": "^4.2.0",
"react-router-dom": "^4.2.2",
"react-router-redux": "^4.0.8",
...
"typescript": "^2.7.2",
"@types/react-redux": "^5.0.15",
"@types/react-router": "^4.0.22",
"@types/react-router-dom": "^4.2.4",
"@types/react": "^16.0.38",
"@types/react-dom": "^16.0.4",
【问题讨论】:
哦,我什至根本没有安装@types
:)
【参考方案1】:
我在我们的项目中是怎么做的(这是最简单的方法,你用的时候就可以得到智能感知):
import * as React from 'react'
import connect from 'react-redux';
import withRouter, RouteComponentProps from 'react-router';
import
fetchTestLists,
newTestList,
displayTestList,
from '../../../actions/index';
interface StateProps
testList: any; // todo: use the type of state.myList to have validation on it
interface DispatchProps
fetchTestLists: () => void;
newTestList: () => void;
displayTestList: (any) => void; // todo: replace any with the actual type
interface Props extends RouteComponentProps // custom properties passed to component
className: string;
type PropsType = StateProps & DispatchProps & Props;
class MyTest extends React.Component<PropsType>
constructor(props)
super(props);
this.handleCellClick = this.handleCellClick.bind(this);
this.newTestList = this.newTestList.bind(this);
componentDidMount()
this.props.fetchTestLists();
handleCellClick(row, column, event)
this.props.displayTestList(row);
newTestList(e)
this.props.newTestList()
render()
return (
<div className=this.props.className>
</div>
);
const mapStateToProps = (state, ownProps: Props): StateProps => (
testList: state.myList, // todo: define a type for the root state to have validation here
);
const dispatchToProps: DispatchProps =
fetchTestLists,
newTestList,
displayTestList,
;
export default withRouter(connect(
mapStateToProps,
dispatchToProps,
)(MyTest));
此外,如果这是您经常输入的内容,我建议您编写自定义 sn-p(如果您使用的是 VS Code 之类的东西,这很容易)。
【讨论】:
【参考方案2】:我尝试重写您的示例并最终得到以下代码:
import * as React from 'react';
import connect from 'react-redux';
import RouteComponentProps, withRouter from 'react-router';
import
fetchTestLists,
newTestList,
displayTestList,
from '../../../actions/index';
import Dispatch, bindActionCreators, AnyAction from 'redux';
interface IStateProps
testList: IListType; // todo: use the type of state.myList to have validation on it
interface IDispatchProps
fetchTestLists: () => AnyAction;
newTestList: () => AnyAction;
displayTestList: (value: string) => AnyAction; // todo: replace any with the actual type
interface IProps // custom properties passed to component
className: string;
type PropsType = IStateProps & IDispatchProps & IProps;
class MyTestComponent extends React.Component<PropsType & RouteComponentProps<>, >
constructor(props: PropsType & RouteComponentProps<>)
super(props);
this.handleCellClick = this.handleCellClick.bind(this);
this.newTestList = this.newTestList.bind(this);
public componentDidMount()
this.props.fetchTestLists();
public handleCellClick(row, column, event)
this.props.displayTestList(row);
public newTestList(e)
this.props.newTestList();
public render(): JSX.Element
return (
<div className=this.props.className>
</div>
);
export const MyTest = connect(
(state: IAppState, ownProps: IProps) => (
testList: state.testList,
...ownProps,
),
(dispatch: Dispatch) => bindActionCreators<AnyAction, Pick<IDispatchProps, keyof IDispatchProps>>(
displayTestList, fetchTestLists, newTestList ,
dispatch,
),
)(withRouter(MyTestComponent));
interface IListType
someProp: string;
interface IAppState
testList: IListType;
differentList: IListType;
我已将 export default 更改为使用 connect 和 withRouter HOC 将包装的 MyTestComponent 类的结果分配到我的测试。然后我像这样导入 MyTest 组件
import MyTest from './MyTest'
我添加了接口来描述从父组件传递的所有属性,还以不同的方式使用 withRouter 和 connect(对我来说更具可读性)。
希望对你有帮助
【讨论】:
【参考方案3】:我通过强制安装 @types/react-redux 包解决了这个问题。我刚刚从 4.4.5 更新到 5.0.15。
运行一个新的npm install --save @types/react-redux@5.0.15
可能是值得的。
【讨论】:
以上是关于如何将 TypeScript 与 withRouter、connect、React.Component 和自定义属性一起使用?的主要内容,如果未能解决你的问题,请参考以下文章
React Router - 更新版本后 withRouter 上的 Typescript 错误
使用“withRouter”和 Typescript 时出现类型问题
react: typescript integrate withRouter
react-router-dom v6 TypeScript withRouter用于类组件[重复]