react router - problem med typer når man bruker "withRouter" og Typescript


Jeg prøver å få litt mer dyp kunnskap og praksis i React+Typescript, og jeg har støtt på denne typefeilen når jeg bruker withRouter fra react-router-dom.

Kodeeksemplet mitt er veldig enkelt, og jeg har forsøkt å finne personer med samme problem, og noen av svarene pekte på en feil med en oppgradering (men de var fra 2016, så...) og noen av dem brukte en connect()-setning som jeg ikke bruker (noe som førte til spørsmålet, "Gjør jeg dette feil fordi jeg ikke bruker det?"). Jeg ser at noen av de forslagene også involverte å mappe Props til State, noe jeg ikke har gjort (eller sett) før nå. Jeg håper noen har forslag til hva jeg mangler og hva annet jeg bør se på.

Koden er:

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);

Det jeg ville forvente herfra er at det skal fungere smidig (jeg må si at jeg først prøvde med en funksjonell komponent siden jeg ikke har noen state, men alle løsningene jeg så involverte en klassekomponent, så jeg flyttet det inn i den), men i stedet får jeg følgende feil på MenuItem i siste linje:

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)

Mine spørsmål er:

  1. Hvorfor står det "type 'typeof MenuItem'"? Bør det ikke bare stå typen til 'MenuItem' i stedet for funksjonen for å få typen?

  2. Er det nødvendig for at withRouter skal fungere med klassekomponenter, eller fungerer det også på funksjonelle komponenter?

  3. Trenger jeg å connect() noe, eller koble Props til State? Hvis ja, hvorfor?

  4. Og til slutt, hvordan kan jeg fikse dette?

Svar #1

Ifølge dokumentasjonen vil withRouter sende oppdaterte match, location og history props til den innpakkede komponenten hver gang den rendres.

Så MenuItem-komponenten bør ha props for å motta dem. Foreløpig har MenuItem-komponenten props av typen ISection, som ikke inkluderer router-props.

Den enkleste måten å legge til ruter-egenskaper på er å krysse ISection med RouteComponentProps.

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

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

Full kode er

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);

Og svar på dine spørsmål

  1. Hvorfor står det "type 'typeof MenuItem'"? Bør det ikke bare stå typen til 'MenuItem' i stedet for funksjonen for å få typen?

    Feil oppstått på grunn av typer som ikke er kompatible. MenuItem er en klasse, ikke en type. For å få typen til MenuItem bør du bruke typeof MenuItem. Så typeof MenuItem er en type. Og kompilatoren sier korrekt, "type typeof MenuItem".

  2. Er det nødvendig for at withRouter skal fungere med klassekomponenter, eller fungerer det også på funksjonelle komponenter?

    Det er tillatt å jobbe med klassekomponenter og med funksjonelle komponenter.

    Slik vil komponenten din se ut hvis den implementeres som en funksjonell

    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. Trenger jeg å connect() noe, eller koble Props til State? Hvis ja, hvorfor?

    Nei, dette er ikke et strengt krav. connect er en del av Redux, så hvis du bruker Redux kan du koble til. Her er dokumentasjonen på hvordan du bruker withRouter med connect. Men igjen, det er ikke nødvendig.

  4. Og til slutt, hvordan kan jeg fikse dette?

    Allerede besvart. Se over :-)

Relaterte artikler