输入一个同时使用 withRouter 和 connect 的 HOC 组件

Posted

技术标签:

【中文标题】输入一个同时使用 withRouter 和 connect 的 HOC 组件【英文标题】:typing a HOC component that's using both withRouter and connect 【发布时间】:2020-10-31 04:01:35 【问题描述】:

我有以下堆栈:

react@16.13.1
react-dom@16.13.1
react-redux@7.2.0
react-router-dom@5.2.0

我按照this answer 中建议的方法对使用withRouter 创建的HOC 组件进行类型检查,并且工作正常。例如。以下组件类型检查:

import React from "react";
import  withRouter  from "react-router-dom";
import  RouteComponentProps  from "react-router-dom";

type Props = RouteComponentProps<any>;

class Test extends React.Component<Props, > 
  componentDidMount = () => 
    this.props.history.push("foo");
  ;

  render() 
    return <div>foo</div>;
  


export default withRouter(Test);

当我还希望使用connect 将组件链接到 Redux 存储时,就会出现问题。以下代码无法进行类型检查:

import React from "react";
import  connect, ConnectedProps  from "react-redux";

import  withRouter  from "react-router-dom";

import  RouteComponentProps  from "react-router-dom";

type RootState =  foo: number ;

const mapStateToProps = (state: RootState) => 
  foo: state.foo;
;

const connector = connect(mapStateToProps, null);

type PropsFromRedux = ConnectedProps<typeof connector>;

type Props = PropsFromRedux & RouteComponentProps<any>;

class Test extends React.Component<Props, > 
  componentDidMount = () => 
    this.props.history.push("foo"); // TS2339: Property 'history' does not exist on type 'never'.
  ;

  render() 
    return <div>foo</div>;
  


export default withRouter(connector(Test));

具体来说,我得到:

TS2339: Property 'history' does not exist on type 'never'.

两个问题:

    为什么props 的类型被评估为never? 如何正确检查使用withRouterconnect 连续应用创建的HOC 组件?

【问题讨论】:

PropsFromReduxRouteComponentProps 都没有提到history @user0101 如果是这种情况,那么第一个代码清单也不会进行类型检查。因此,RouteComponentProps 确实声明了history。但不知何故,当RouteComponentProps 类型与ConnectedProps 的输出相结合时,我们就会遇到我无法解释的类型检查问题。 【参考方案1】:

从不

如果您开始将鼠标悬停在一些道具上,您会看到有很多 never 正在进行。

Props 是 never,因为 PropsFromReduxnevernever 的并集总是 never)。

type ConnectedProps<TConnector> = TConnector extends InferableComponentEnhancerWithProps<infer TInjectedProps, any> ? TInjectedProps : never

如上所示,ConnectedProps 实用程序类型返回InferableComponentEnhancerWithProps 的第一个泛型。

PropsFromRedux,即ConnectedProps&lt;typeof connector&gt;,计算结果为never,因为connected 的类型是InferableComponentEnhancerWithProps&lt;never, &gt;

所以向后追溯,我们看到核心问题是connected的类型。

无效

您实际上在 mapStateToProps 函数中犯了一个错误,因为您忘记将它括在括号中,所以您实际上并没有返回任何内容!仅此一项并不能解决您的问题(它只是将 never 替换为 void),但它是其他修复的先决条件。

const mapStateToProps = (state: RootState) => (
  foo: state.foo;
);

现在它返回 foo: number 而不是 void

解决方案 1

connected 的推断类型出错的原因是您将dispatch 参数设置为nullconnect 函数有 14 different overloads,但唯一允许第二个参数为 null 的是设置第三或第四个参数的那些。如果您不需要设置第二个 dispatch 参数,则应该完全不使用它。

当我们去掉 null 参数时,突然一切都好了,this.props.history 得到类型 History&lt;any&gt;

const connector = connect(mapStateToProps);
// => InferableComponentEnhancerWithProps<foo: number & DispatchProp<AnyAction>, >

type PropsFromRedux = ConnectedProps<typeof connector>;
// => foo: number & DispatchProp<AnyAction>

type Props = PropsFromRedux & RouteComponentProps<any>;
// => foo: number & DispatchProp<AnyAction> & RouteComponentProps<any, StaticContext, any>

解决方案 2

我喜欢我的typesinterfaces 独立于我的代码,所以我不喜欢typeof connector。我们知道我们想从 redux 中提取什么 props,所以我们可以直接输入它们。

type PropsFromRedux = 
  foo: number;
  dispatch: Dispatch;

Typescript Playground Link

【讨论】:

以上是关于输入一个同时使用 withRouter 和 connect 的 HOC 组件的主要内容,如果未能解决你的问题,请参考以下文章

使用“withRouter”和 Typescript 时出现类型问题

connect 和 withRouter 问题

withRouter的作用和一个简单应用

React Context API + withRouter - 我们可以一起使用它们吗?

测试包含在 withRouter 中的反应组件(最好使用 jest/enzyme)

在 react 中使用 react-router 4 和 styled-component 时,不应在 Router 外部使用 Route 或 withRouter()