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

Posted

技术标签:

【中文标题】使用“withRouter”和 Typescript 时出现类型问题【英文标题】:issue with types when using "withRouter" and Typescript 【发布时间】:2019-11-20 13:42:49 【问题描述】:

我正在尝试对 React+Typescript 进行更深入的了解和实践,在使用来自 react-router-domwithRouter 时遇到了这个打字错误。

我的代码 sn-p 非常简单,我尝试找出有相同问题的人,其中一些答案指出升级时出错(但它们是从 2016 年开始的,所以......)和他们中的一些人使用了我没有使用的connect() 语句(这导致了一个问题,“我这样做是因为不使用它而做错了吗?”)。我看到其中一些建议还涉及将 Props 映射到 State,直到现在我还没有做过(也没有见过)。我希望有人对我缺少什么以及我应该查看的其他内容提出一些建议。

代码是:

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

interface ISection 
  id: number;
  title: string;
  imageUrl: string;
  size: string;


class MenuItem extends React.Component<ISection> 
  render() 
    return (
      <div className=`$this.props.size menu-item`>
        <div
          className="background-image"
          style= backgroundImage: `url($this.props.imageUrl)` 
        />
        <div className="content">
          <h1 className="title">this.props.title</h1>
          <span className="subtitle">some subtitle</span>
        </div>
      </div>
    );
  


export default withRouter(MenuItem);

我期望从这里顺利工作(我不得不说我首先尝试了一个功能组件,因为我没有任何状态,但是我看到的所有解决方案都涉及一个类组件,所以我移动了它进入它),但我在最后一行的MenuItem 上收到以下错误:

Argument of type 'typeof MenuItem' is not assignable to parameter of type 'ComponentClass<RouteComponentProps<any, StaticContext, any>, any> | FunctionComponent<RouteComponentProps<any, StaticContext, any>> | (FunctionComponent<RouteComponentProps<any, StaticContext, any>> & ComponentClass<...>) | (ComponentClass<...> & FunctionComponent<...>)'.
  Type 'typeof MenuItem' is not assignable to type 'ComponentClass<RouteComponentProps<any, StaticContext, any>, any>'.
    Types of parameters 'props' and 'props' are incompatible.
      Type 'RouteComponentProps<any, StaticContext, any>' is missing the following properties from type 'Readonly<ISection>': id, title, imageUrl, sizets(2345)

我的问题是:

    为什么会显示“type 'typeof MenuItem'”?不应该只说'MenuItem'的类型而不是获取类型的函数吗?

    withRouter 是否必须与类组件一起使用,还是对功能组件也有效?

    我需要connect() 什么,或者将 Props 映射到 State 上吗?如果是,为什么?

    最后,我该如何解决这个问题?

【问题讨论】:

【参考方案1】:

从 documentation 开始,withRouter 将在渲染时将更新的 matchlocationhistory 道具传递给包装的组件。

所以MenuItem 组件应该有道具来接收它们。目前,MenuItem 组件的 props 类型为 ISection,不包括路由器 props。

添加路由器道具的最简单方法是将ISectionRouteComponentProps 相交。

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

// ...
class MenuItem extends React.Component<ISection & RouteComponentProps> 

完整代码是

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

interface ISection 
    id: number;
    title: string;
    imageUrl: string;
    size: string;


class MenuItem extends React.Component<ISection & RouteComponentProps> 
    render() 
        return (
            <div className=`$this.props.size menu-item`>
                <div
                    className="background-image"
                    style= backgroundImage: `url($this.props.imageUrl)` 
                />
                <div className="content">
                    <h1 className="title">this.props.title</h1>
                    <span className="subtitle">some subtitle</span>
                </div>
            </div>
        );
    


export default withRouter(MenuItem);

并回答您的问题

    为什么说“type 'typeof MenuItem'”?不应该只说'MenuItem'的类型而不是获取类型的函数吗?

    由类型不兼容引起的错误。 MenuItem 是类,而不是类型。要获取MenuItem 的类型,您应该使用typeof MenuItem。所以typeof MenuItem 是类型。编译器正确地说,“输入typeof MenuItem”。

    withRouter 是否需要对类组件起作用,还是对函数组件也起作用?

    允许与类组件和功能组件一起使用。

    这就是你的组件在实现为函数式时的样子

    const Cmp1: React.FunctionComponent<ISection & RouteComponentProps> = (props) => 
        return (
            <div className=`$props.size menu-item`>
                <div
                    className="background-image"
                    style= backgroundImage: `url($props.imageUrl)` 
                />
                <div className="content">
                    <h1 className="title">props.title</h1>
                    <span className="subtitle">some subtitle</span>
                </div>
            </div>
        );
    
    
    const WrappedCmp = withRouter(Cmp1);
    

    我需要connect() 什么,或者将 Props 映射到 State 上吗?如果有,为什么?

    不,这不是严格的要求。 connect 是 Redux 的一部分,所以如果你使用 Redux,你可以连接。这是documentation 如何使用withRouterconnect。但同样,它不是必需的。

    最后,我该如何解决这个问题?

    已经回答了。见上文:-)

【讨论】:

哇@Fyodor,这是我期待的最佳答案,非常感谢! 你不会相信这个问题有多少令人费解的答案,而这个是最简洁和格式正确的,@Fyodor 是赢家 @P.Gracia 哇哦!解释清楚【参考方案2】:

对于任何来这里的人 Next.js 像这样将你的道具界面与 WithRouterProps 相交..

import WithRouterProps from "next/dist/client/with-router";

class MenuItem extends React.Component&lt;IProps &amp; WithRouterProps&gt;

【讨论】:

以上是关于使用“withRouter”和 Typescript 时出现类型问题的主要内容,如果未能解决你的问题,请参考以下文章

使用 connect 和 withRouter 包装组件的顺序是不是重要

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

typeScrip泛型

带连接的 withRouter 在 v5.0.0 中不起作用?

withRouter 参数解析 Invariant Violation 时出错:您不应在 <Router> 之外使用 <Route> 或 withRouter()

withRouter的作用和一个简单应用