Я пытаюсь получить более глубокие знания и попрактиковаться в 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)
Мои вопросы:
Почему написано "тип" typeof MenuItem"? Разве он не должен просто указать тип «MenuItem» вместо функции для получения типа?
Нужно ли, чтобы withRouter работал с классовыми компонентами, или он также работает с функциональными компонентами?
Нужно ли мне что-то connect() или сопоставлять реквизиты с состоянием? Если да, то почему?
И, наконец, как я могу это исправить?






Начиная с документация, 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);
И ответы на ваши вопросы
Почему написано "тип" 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() или сопоставлять реквизиты с состоянием? Если да, то почему?
Нет, это не жесткое требование. connect является частью Redux, поэтому, если вы используете Redux, вы можете подключиться. Вот документация того, как использовать withRouter с connect. Но опять же, это не требуется.
И, наконец, как я могу это исправить?
Уже ответил. Смотри выше :-)
Вы не поверите, сколько запутанных ответов существует именно на этот вопрос, и этот самый лаконичный и хорошо отформатированный, @Fyodor - победитель
@P.Gracia Вау! Хорошо объяснил
Для тех, кто приходит сюда для Next.js, пересекайте интерфейс вашего реквизита вместе с WithRouterProps, вот так..
import { WithRouterProps } from "next/dist/client/with-router";
class MenuItem extends React.Component<IProps & WithRouterProps>
Вау @Fyodor, это лучший ответ, который я мог ожидать, большое спасибо!