Проблема с типами при использовании withRouter и Typescript

Я пытаюсь получить более глубокие знания и попрактиковаться в React + Typescript, и я столкнулся с этой опечаткой при использовании withRouter из react-router-dom.

Мой фрагмент кода очень прост, и я пытался найти людей с такой же проблемой, и некоторые ответы указывали на ошибку с обновлением (но они были с 2016 года, так что...), а некоторые из них использовали утверждение connect(), которое я не использую (что приводит к вопросу: «Я делаю это неправильно, потому что не использую его?»). Я вижу, что некоторые из этих предложений также включали сопоставление свойств с состоянием, чего я не делал (и не видел) до сих пор. Я надеюсь, что у кого-то есть некоторые предложения о том, что мне не хватает и на что еще я должен смотреть.

Код:

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. Почему написано "тип" typeof MenuItem"? Разве он не должен просто указать тип «MenuItem» вместо функции для получения типа?

  2. Нужно ли, чтобы withRouter работал с классовыми компонентами, или он также работает с функциональными компонентами?

  3. Нужно ли мне что-то connect() или сопоставлять реквизиты с состоянием? Если да, то почему?

  4. И, наконец, как я могу это исправить?

Зод: сила проверки и преобразования данных
Зод: сила проверки и преобразования данных
Сегодня я хочу познакомить вас с библиотекой Zod и раскрыть некоторые ее особенности, например, возможности валидации и трансформации данных, а также...
Как заставить Remix работать с Mantine и Cloudflare Pages/Workers
Как заставить Remix работать с Mantine и Cloudflare Pages/Workers
Мне нравится библиотека Mantine Component , но заставить ее работать без проблем с Remix бывает непросто.
Угловой продивер
Угловой продивер
Оригинал этой статьи на турецком языке. ChatGPT используется только для перевода на английский язык.
TypeScript против JavaScript
TypeScript против JavaScript
TypeScript vs JavaScript - в чем различия и какой из них выбрать?
Синхронизация localStorage в масштабах всего приложения с помощью пользовательского реактивного хука useLocalStorage
Синхронизация localStorage в масштабах всего приложения с помощью пользовательского реактивного хука useLocalStorage
Не все нужно хранить на стороне сервера. Иногда все, что вам нужно, это постоянное хранилище на стороне клиента для хранения уникальных для клиента...
Что такое ленивая загрузка в Angular и как ее применять
Что такое ленивая загрузка в Angular и как ее применять
Ленивая загрузка - это техника, используемая в Angular для повышения производительности приложения путем загрузки модулей только тогда, когда они...
14
0
8 607
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Начиная с документация, withRouter будет передавать обновленные реквизиты match, location и history обернутому компоненту при каждом его рендеринге.

Поэтому компонент MenuItem должен иметь реквизиты для их получения. На данный момент компонент MenuItem имеет реквизиты типа ISection, которые не включают реквизиты маршрутизатора.

Самый простой способ добавить реквизиты маршрутизатора — пересечь ISection с RouteComponentProps.

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. Почему написано "тип" 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() или сопоставлять реквизиты с состоянием? Если да, то почему?

    Нет, это не жесткое требование. connect является частью Redux, поэтому, если вы используете Redux, вы можете подключиться. Вот документация того, как использовать withRouter с connect. Но опять же, это не требуется.

  4. И, наконец, как я могу это исправить?

    Уже ответил. Смотри выше :-)

Вау @Fyodor, это лучший ответ, который я мог ожидать, большое спасибо!

P.Gracia 11.07.2019 08:30

Вы не поверите, сколько запутанных ответов существует именно на этот вопрос, и этот самый лаконичный и хорошо отформатированный, @Fyodor - победитель

Cameron 10.10.2019 23:48

@P.Gracia Вау! Хорошо объяснил

Khaled Rakhisi 06.11.2021 21:14

Для тех, кто приходит сюда для Next.js, пересекайте интерфейс вашего реквизита вместе с WithRouterProps, вот так..

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

class MenuItem extends React.Component<IProps & WithRouterProps>

Другие вопросы по теме