reactjs - React - utilizarea forwardRef în componentă, eroare typescript - Lipsă proprietatea 'current'


Îmi este greu să înțeleg de ce compilatorul TypeScript este nemulțumit de forwardRef pe care îl folosesc într-un component.

Am redus codul meu la strictul necesar pentru exemplul de mai jos, asigurându-mă că această eroare de TypeScript este singura care apare în cod, conform problemei din codul meu real:

import { createRef, forwardRef, RefObject } from 'react';

const MyComponent = () => {
    const selectCountryRef = createRef<HTMLSelectElement>();

    return (
        <div>
            <SelectCountry ref={selectCountryRef} />
        </div>
    );
};

const SelectCountry = forwardRef((ref?: RefObject<HTMLSelectElement>) => {
    return (
        <FormFieldSelect ref={ref} />
    );
});

const FormFieldSelect = ({ ref }) => {
    return (
        <div>
            <select ref={ref}>
            </select>
        </div>
    );
};

Când încerc să compilez asta, primesc eroarea:

Property 'current' is missing in type '{ ref: RefObject<HTMLSelectElement>; }' but required in type 'RefObject<HTMLSelectElement>'.

În VSCode, <SelectCountry ... este locul unde IDE-ul are o plângere, mesajul exact este:

const SelectCountry: ForwardRefExoticComponent<RefObject<HTMLSelectElement> & RefAttributes<unknown>> Property 'current' is missing in type '{ ref: RefObject<HTMLSelectElement>; }' but required in type 'RefObject<HTMLSelectElement>'.ts(2741) index.d.ts(85, 18): 'current' is declared here.

Pur și simplu nu reușesc să înțeleg de ce primesc această eroare.

În prezent folosind React 17.0.2

Răspuns #1

forwardRef este apelat mai întâi cu argumentul props și al doilea cu argumentul ref.

Funcția ta ForwardRefRenderFunction folosește ref?: RefObject<HTMLSelectElement> ca primul argument, care reprezintă props-urile. Din perspectiva TypeScript, tipul props-urilor pentru SelectCountry este un RefObject<HTMLSelectElement>. De aceea primești eroarea:

Proprietatea 'current' lipsește în tipul { ref: RefObject<HTMLSelectElement>; } dar este necesară în tipul RefObject<HTMLSelectElement>.


Răspunsul lui @jeffreyquan îți arată o soluție cu tipuri corecte, așa că nu voi relua tot ce au scris. Poți să atribui tipuri pentru props și ref setând parametrii de tip generici pe funcția forwardRef așa cum ți-au arătat.

De asemenea, poți atribui tipurile prin notarea argumentelor props și ref ale funcției interioare așa cum încercai să faci, dar trebuie să te asiguri că este făcut corect. Dacă nu atribui niciun tip la props, atunci acesta este inferat ca PropsWithChildren<{}>, ceea ce este de fapt în regulă aici. Deci, aceasta funcționează:

const SelectCountry = forwardRef((props, ref: ForwardedRef<HTMLSelectElement>) => {
    return (
        <FormFieldSelect ref={ref} />
    );
});

Pentru a trece verificarea tipurilor în modul strict, trebuie să schimbi RefObject cu ForwardedRef. Aceasta permite utilizarea referințelor de tip callback și null.

Răspunsul #2

Încearcă următoarele. Am adăugat un useEffect și un console.log în MyComponent pentru a arăta că funcționează. Eram confuz în legătură cu modul în care ai atribuit ref?: RefObject<HTMLSelectElement> în SelectCountry și cred că acesta a cauzat eroarea. Am arătat cum să atribui tipuri pentru forwardRef în fragmentul de cod, adică forwardRef<RefElementType,ComponentPropsType>. De asemenea, FormFieldSelect trebuie să fie un component forwardRef.

import React, { useEffect, createRef, forwardRef } from 'react'

export const MyComponent = () => {
  const selectCountryRef = createRef<HTMLSelectElement>()

  useEffect(() => {
    console.log(selectCountryRef.current)
  }, [selectCountryRef])

  return (
    <div>
      <SelectCountry ref={selectCountryRef} />
    </div>
  )
}

interface SelectCountryProps {}

const SelectCountry = forwardRef<HTMLSelectElement, SelectCountryProps>(
  (props, ref) => {
    return <FormFieldSelect ref={ref} />
  },
)

interface FormFieldSelectProps {}

const FormFieldSelect = forwardRef<HTMLSelectElement, FormFieldSelectProps>(
  (props, ref) => {
    return (
      <div>
        <select ref={ref}></select>
      </div>
    )
  },
)