Jaki jest prawidłowy typ ref w Reakcie?

85

Przechowuję referencję w moim sklepie redux i używam mapStateToProps do ujawnienia referencji dla komponentów, które potrzebują do niego dostępu.

Zapisany ref wygląda następująco:

ref={node => this.myRefToBePutInReduxGlobalStore = node}

Jaki jest poprawny typ propType dla tego odniesienia?

danday74
źródło
8
Ponieważ ref nie można serializować, umieszczanie ich w sklepie Redux nie jest dobrym pomysłem. Niemniej jednak wartość przekazywana do właściwości refjest funkcją, której pierwszy argument jest odniesieniem do elementu DOM lub null. To funkcja .
rishat
5
Nie możesz użyć propType dla refs, ale możesz go użyć do props. Ponieważ przekazujesz go do sklepu Redux, jest on przedstawiony jako proppodobny this.props.myRefToBePutInReduxGlobalStore. myRefToBePutInReduxGlobalStorepowinien być typem węzła, więc PropTypes.nodepowinien działać dla Ciebie
The Reason
Myślę, że powinno tak być, PropType.objectponieważ ref jest w zasadzie instancją klasy, która jest obiektem. Stąd, gdy przekazujesz element ref jako rekwizyty, byłby to typ obiektu
Hriday Modi

Odpowiedzi:

96

TL; DR

Jeśli chcesz wpisać ref, który oczekuje tylko natywnych elementów DOM, takich jak a divlub an input, prawidłowa definicja jest następująca:

refProp: PropTypes.oneOfType([
    // Either a function
    PropTypes.func, 
    // Or the instance of a DOM native element (see the note about SSR)
    PropTypes.shape({ current: PropTypes.instanceOf(Element) })
])

Odpowiedz na konkretny problem opisany w oryginalnym poście

W przykładzie pytania OP to nie typ rekwizytu ref musi być zadeklarowany, ale rzeczy wskazywane przez ref, które zostaną przekazane przy użyciu reduxu mapStateToProps. Typ rekwizytu dla elementu DOM to: myRefToBePutInReduxGlobalStore: PropTypes.instanceOf(Element)(jeśli jest to element DOM). Chociaż chciałbym:

  1. Zmień nazwę na myElementToBePutInReduxGlobalStore(element nie jest odniesieniem)
  2. Unikaj przechowywania danych, których nie można serializować, w magazynie Redux
  3. Unikaj przekazywania elementów w rekwizytach, jak wyjaśnił inżynier React Seb Markbage

Długa odpowiedź na aktualne pytanie

P: Jaki jest poprawny typ ref w React?

Przykład użycia:

function FancyInput({ inputRef }) {
    return (
        <div className="fancy">
            Fancy input
            <input ref={inputRef} />
        </div>
    )
}

FancyInput.propTypes = {
    inputRef: ???   // What is the correct prop type here?
}

function App(props) {
    const inputRef = React.useRef()
    useLayoutEffect(function focusWhenStuffChange() {
        inputRef.current?.focus()
    }, [props.stuff])
    return <FancyInput inputRef={inputRef} />
}

Obecnie istnieją dwa rodzaje referencji:

  1. Obiekt, który wygląda tak:, { current: [something] }zwykle utworzony przez React.createRef()pomocnika lub React.useRef()hook
  2. Funkcja zwrotna, która otrzyma [something]jako argument (jak w przykładzie OP)

Uwaga: historycznie można było również użyć ref w postaci ciągu znaków, ale jest to uważane za starsze i zostanie usunięte z reakcji

Druga pozycja jest bardzo prosta i wymaga następującego typu prop: PropTypes.func.

Pierwsza opcja jest mniej oczywista, ponieważ możesz chcieć określić typ elementu wskazywanego przez ref.

Wiele dobrych odpowiedzi

ref może wskazywać na coś innego niż element DOM.

Jeśli chcesz być całkowicie elastyczny:

refProp: PropTypes.oneOfType([
    PropTypes.func, 
    PropTypes.shape({ current: PropTypes.any })
])

Powyższe wymusza tylko kształt obiektu referencyjnego z currentwłaściwością. Będzie działać przez cały czas dla każdego typu ref. W celu używania typu rekwizytu, który jest sposobem na wykrycie wszelkich niespójności podczas tworzenia , prawdopodobnie to wystarczy. Wydaje się bardzo mało prawdopodobne, aby obiekt posiadający kształt { current: [any] }i przekazany do refToForwardrekwizytu nie był rzeczywistym odniesieniem.

Jednak możesz chcieć zadeklarować, że twój komponent nie oczekuje żadnego rodzaju referencji, ale tylko określonego typu, biorąc pod uwagę, do czego potrzebuje tego ref.

Skonfigurowałem piaskownicę pokazującą kilka różnych sposobów deklarowania referencji, nawet niestandardowych, a następnie testowałem je z wieloma typami rekwizytów. Znajdziesz go tutaj .


Jeśli tylko oczekiwać wskazując ref do natywnego elementu wejściowego, nie każdy HTML Element:

refProp: PropTypes.oneOfType([
    PropTypes.func, 
    PropTypes.shape({ current: PropTypes.instanceOf(HTMLInputElement) })
])

Jeśli spodziewasz się tylko odniesienia wskazującego na komponent klasy React:

refProp: PropTypes.oneOfType([
    PropTypes.func, 
    PropTypes.shape({ current: PropTypes.instanceOf(Component) })
])

Jeśli spodziewasz się tylko odniesienia wskazującego na komponent funkcjonalny, używając useImperativeHandledo ujawnienia jakiejś metody publicznej:

refProp: PropTypes.oneOfType([
    PropTypes.func, 
    PropTypes.shape({ current: PropTypes.object })
])

Uwaga: powyższy typ właściwości jest interesujący, ponieważ obejmuje również komponenty reagujące i natywne elementy DOM, ponieważ wszystkie dziedziczą podstawowy kod javascript Object


Nie ma jednego dobrego sposobu na zadeklarowanie typu właściwości referencyjnej, zależy to od użycia. Jeśli Twoje odniesienie wskazuje na coś bardzo określonego, warto dodać określony typ rekwizytu. W przeciwnym razie wystarczy sprawdzić ogólny kształt.


Uwaga dotycząca renderowania po stronie serwera

Jeśli twój kod ma działać na serwerze, chyba że masz już polyfill środowiska DOM, Elementlub inny typ po stronie klienta będzie undefinedw NodeJS. Możesz użyć następującej podkładki, aby ją podeprzeć:

Element = typeof Element === 'undefined' ? function(){} : Element

Poniższe strony React Docs dają więcej informacji o tym, jak używać referencji, zarówno obiektu, jak i wywołań zwrotnych , a także jak używać useRefhook

Odpowiedź zaktualizowana dzięki @Rahul Sagore, @Ferenk Kamra, @svnm i @Kamuela Franco

Pandaiolo
źródło
2
To daje Elementnie jest zdefiniowane w renderowaniu po stronie serwera. Jakieś rozwiązanie?
Rahul Sagore
Słuszna uwaga. A co z tą podkładką: Element = typeof Element === 'undefined' ? function(){} : Element(zasugerowana przez Ryana Florence w jakimś wydaniu na githubie). Jeśli to działa, mogę dodać to do mojej odpowiedzi.
Pandaiolo
Rozgryzłem to i dodałem to if (!isBrowser) { global.Element = null; }. Pracował dla mnie. isBrowser = typeof window !== 'undefined';
Rahul Sagore
Świetna odpowiedź. Chciałbym, żeby TL; DR był na szczycie odpowiedzi.
Kamuela Franco
12

Bardzo podobny do posta @ Pandaiolo,

PropTypes.elementType został dodany

forwardedRef: PropTypes.oneOfType([
  PropTypes.func,
  PropTypes.shape({ current: PropTypes.elementType })
]),

W przypadku korzystania z PropTypes> = 15.7.0 PropTypes.elementTypezostało dodane 10 lutego 2019 r. W tym żądaniu

svnm
źródło
Dzięki @svnm, zaktualizowałem moją odpowiedź, aby to odzwierciedlić, co upraszcza!
Pandaiolo
1
W końcu elementTypeto nie zadziałało, więc przetestowałem kilka typów rekwizytów na różnych rodzajach komponentów, które mogą być wskazywane przez ref, w piaskownicy. Oto wyniki: codeandbox.io/s/loving-benz-w6fbh . Nie jestem pewien, czy omówiłem wszystkie możliwości, ale wydaje się, że poprawnym typem właściwości elementu DOM jest instanceOf(Element)(lub object), dla klasy to jest instanceOf(Component)(lub object) i dla konkretnego przypadku użycia komponentu funkcjonalnego, który useImperativeHandlego używa object. Myśli?
Pandaiolo,
9

Sprawdzam tylko preferowany sposób, a ponieważ PropType.object nie jest zbyt wartościowy, ostatecznie użyłem tego:

PropTypes.shape({ current: PropTypes.instanceOf(Element) })
Ferenc Kamras
źródło
1

Sprawdziłem wszystkie powyższe odpowiedzi, ale otrzymałem ostrzeżenie od reakcji, więc dużo szukałem i otrzymałem właściwą odpowiedź.

import React, { Component } from 'react';

ComponentName.propTypes = {
  reference: PropTypes.oneOfType([
    PropTypes.func,
    PropTypes.shape({ current: PropTypes.instanceOf(Component) }),
  ]),
};
Nisharg Shah
źródło