【问题标题】:issue with types when using "withRouter" and Typescript使用“withRouter”和 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)

我的问题是:

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

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

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

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

【问题讨论】:

    标签: typescript react-router react-router-dom


    【解决方案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);
    

    并回答您的问题

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

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

    2. 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);
      
    3. 我需要connect() 什么,或者将 Props 映射到 State 上吗?如果有,为什么?

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

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

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

    【讨论】:

    • 哇@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;

    【讨论】:

      猜你喜欢
      • 2019-06-12
      • 2023-01-22
      • 1970-01-01
      • 2020-10-09
      • 2021-08-25
      • 1970-01-01
      • 1970-01-01
      • 2020-09-10
      相关资源
      最近更新 更多