Qual è il corretto proptype per un ref in React?


87

Sto memorizzando un ref nel mio redux store e utilizzo mapStateToProps per esporre il ref per i componenti che devono accedervi.

Il riferimento memorizzato ha il seguente aspetto:

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

Qual è il propType corretto per questo riferimento?


8
Poiché i ref non sono serializzabili, non è una buona idea metterli in Redux Store. Tuttavia, il valore passato a refprop è una funzione con il primo argomento un riferimento a un elemento DOM o null. È una funzione .
rishat

5
Non puoi usarlo propType per refs, ma puoi usarlo per props. Dato che lo stai passando a redux store, è rappresentato come un propsimile this.props.myRefToBePutInReduxGlobalStore. myRefToBePutInReduxGlobalStoredovrebbe essere il tipo di nodo, quindi PropTypes.nodedovrebbe funzionare per te
The Reason

Penso che dovrebbe essere PropType.objectperché ref è fondamentalmente un'istanza di classe che è un oggetto. Quindi, quando passi l'elemento ref come oggetti di scena, sarebbe di tipo oggetto
Hriday Modi

Risposte:


99

TL; DR

Se vuoi inserire un ref che si aspetta solo elementi DOM nativi, come a divo an input, la definizione corretta è la seguente:

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) })
])

Rispondi al problema specifico descritto nel post originale

Nell'esempio della domanda OP, non è il tipo di prop ref che deve essere dichiarato, ma il materiale indicato dal ref, e che verrà passato da redux usando mapStateToProps. Il tipo di prop per un elemento DOM sarebbe: myRefToBePutInReduxGlobalStore: PropTypes.instanceOf(Element)(se è un elemento DOM). Anche se vorrei:

  1. Rinominalo in myElementToBePutInReduxGlobalStore(un elemento non è un riferimento)
  2. Evita di archiviare dati non serializzabili all'interno di Redux Store
  3. Evita di passare elementi negli oggetti di scena come spiegato dall'ingegnere di React Seb Markbage

Risposta lunga alla domanda reale

D: Qual è il proptype corretto per un ref in React?

Caso d'uso di esempio:

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} />
}

Oggi esistono due tipi di ref in Reaction:

  1. Un oggetto che assomiglia a questo:, { current: [something] }solitamente creato da React.createRef()helper o React.useRef()hook
  2. Una funzione di callback che riceverà [something]come argomento (come quella nell'esempio OP)

Nota: storicamente, potresti anche usare una stringa ref, ma è considerata legacy e verrà rimossa da react

Il secondo elemento è molto semplice e richiede il seguente tipo di prop: PropTypes.func.

La prima opzione è meno ovvia perché potresti voler specificare il tipo di elemento puntato dal rif.

Molte buone risposte

ref può puntare a qualcos'altro oltre all'elemento DOM.

Se vuoi essere totalmente flessibile:

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

Quanto sopra impone solo la forma dell'oggetto ref con currentproprietà. Funzionerà in ogni momento per qualsiasi tipo di ref. Allo scopo di utilizzare il tipo di prop, che è un modo per individuare eventuali incongruenze durante lo sviluppo , questo è probabilmente sufficiente. Sembra molto improbabile che un oggetto con una forma { current: [any] }, passato a un refToForwardsostegno, non sia un vero riferimento.

Tuttavia , potresti voler dichiarare che il tuo componente non si aspetta alcun tipo di ref, ma solo un certo tipo, dato ciò per cui ha bisogno di quel ref.

Ho impostato una sandbox che mostra alcuni modi diversi per dichiarare un ref, anche alcuni non convenzionali, e poi li ho testati con molti tipi di prop. Puoi trovarlo qui .


Se ti aspetti solo un riferimento che punta a un elemento di input nativo, non a qualsiasi HTML Element:

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

Se ti aspetti solo un ref che punta a un componente della classe React:

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

Se ti aspetti solo un riferimento che punta a un componente funzionale utilizzando useImperativeHandleper esporre un metodo pubblico:

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

Nota: il tipo di prop sopra è interessante perché copre anche i componenti di reazione e gli elementi DOM nativi, perché ereditano tutti il ​​javascript di base Object


Non esiste un unico buon modo per dichiarare il tipo di prop per un ref, dipende dall'uso. Se il tuo riferimento punta a qualcosa di molto identificato, può valere la pena aggiungere un tipo di oggetto specifico. Altrimenti, basta controllare la forma generale.


Nota sul rendering lato server

Se il codice deve essere eseguito sul server, a meno che non si disponga già di un ambiente DOM polyfill Elemento di qualsiasi altro tipo lato client, sarà presente undefinedin NodeJS. È possibile utilizzare il seguente spessore per supportarlo:

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

Le seguenti pagine di React Docs forniscono maggiori informazioni su come usare refs, sia object che callback , e anche come usare useRefhook

Risposta aggiornata grazie a @Rahul Sagore, @Ferenk Kamra, @svnm e @Kamuela Franco


2
Elementnon è definito sul rendering lato server. Qualche soluzione a questo?
Rahul Sagore

Buon punto. Che dire di questo shim: Element = typeof Element === 'undefined' ? function(){} : Element(suggerito da Ryan Florence in qualche numero di GitHub). Se funziona per te, posso aggiungerlo alla mia risposta.
Pandaiolo

L'ho capito e ho aggiunto questo if (!isBrowser) { global.Element = null; }. Ha funzionato per me. isBrowser = typeof window !== 'undefined';
Rahul Sagore

Bella risposta. Mi piacerebbe che TL; DR fosse in cima alla risposta.
Kamuela Franco

12

Molto simile al post di @ Pandaiolo,

PropTypes.elementType è stato ora aggiunto

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

Se si utilizza PropTypes> = 15.7.0 è PropTypes.elementTypestato aggiunto il 10 febbraio 2019 in questo PR


Grazie @svnm, ho aggiornato la mia risposta per riflettere questo, il che lo rende più semplice!
Pandaiolo

1
Alla fine, elementTypenon ha funzionato per me, quindi ho effettivamente testato un sacco di tipi di oggetti di scena su diversi tipi di componenti che possono essere puntati da un riferimento, in una sandbox. Ecco i risultati: codesandbox.io/s/loving-benz-w6fbh . Non sono sicuro di aver coperto tutte le possibilità, ma sembra che il proptype corretto per l'elemento DOM sia instanceOf(Element)(or object), per una classe è instanceOf(Component)(o object) e per il caso d'uso specifico di un componente funzionale che lo utilizza useImperativeHandlelo è object. Pensieri?
Pandaiolo

9

Controllo solo il modo preferito e poiché PropType.object non è molto prezioso, ho finito per usare questo:

PropTypes.shape({ current: PropTypes.instanceOf(Element) })

1

Ho controllato tutte le risposte di cui sopra ma ricevo un avvertimento dalla reazione, quindi ho studiato molto e ho ottenuto la risposta giusta.

import React, { Component } from 'react';

ComponentName.propTypes = {
  reference: PropTypes.oneOfType([
    PropTypes.func,
    PropTypes.shape({ current: PropTypes.instanceOf(Component) }),
  ]),
};
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.