¿Cuál es el tipo de propiedad correcto para una referencia en React?

85

Estoy almacenando una referencia en mi tienda redux y uso mapStateToProps para exponer la referencia de los componentes que necesitan acceso a ella.

La referencia que se almacena se parece a:

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

¿Cuál es el propType correcto para esta referencia?

danday74
fuente
8
Debido a que las referencias no se pueden serializar, no es una buena idea ponerlas en la tienda Redux. No obstante, el valor pasado a refprop es una función con el primer argumento una referencia al elemento DOM o nulo. Es una función .
rishat
5
No se puede utilizar propType para refs, pero se puede utilizar para props. Dado que lo está pasando a la tienda redux, entonces se representa como un me propgusta this.props.myRefToBePutInReduxGlobalStore. myRefToBePutInReduxGlobalStoredebe ser el tipo de nodo, por lo que PropTypes.nodedebería funcionar para usted
la razón
Creo que debería ser PropType.objectporque ref es básicamente una instancia de clase que es un objeto. Por lo tanto, cuando pase el elemento ref como accesorios, sería de tipo de objeto
Hriday Modi

Respuestas:

96

TL; DR

Si desea especificar un tipo de referencia que solo espera elementos DOM nativos, como a divo an input, la definición correcta es la siguiente:

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

Respuesta al problema específico descrito en la publicación original.

En el ejemplo de la pregunta OP, no es el tipo ref prop lo que debe declararse, sino las cosas señaladas por la referencia, y que se pasarán desde el uso de redux mapStateToProps. El tipo de prop para un elemento DOM sería: myRefToBePutInReduxGlobalStore: PropTypes.instanceOf(Element)(si es un elemento DOM). Aunque yo haría:

  1. Cambiarle el nombre a myElementToBePutInReduxGlobalStore(un elemento no es una referencia)
  2. Evite almacenar datos no serializables dentro de la tienda redux
  3. Evite pasar elementos en accesorios como lo explicó el ingeniero de React Seb Markbage

Respuesta larga a la pregunta real

P: ¿Cuál es el tipo de propiedad correcto para un árbitro en React?

Ejemplo de caso de uso:

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

Hoy en día, existen dos tipos de referencias en react:

  1. Un objeto que se parece a esto:, { current: [something] }generalmente creado por React.createRef()ayudante o React.useRef()gancho
  2. Una función de devolución de llamada que recibirá [something]como argumento (como la del ejemplo OP)

Nota: históricamente, también podría usar una referencia de cadena, pero se considera heredada y se eliminará de reaccionar

El segundo elemento es bastante simple y requiere el siguiente tipo de accesorio: PropTypes.func .

La primera opción es menos obvia porque es posible que desee especificar el tipo de elemento señalado por la ref.

Muchas buenas respuestas

ref puede apuntar a algo más que al elemento DOM.

Si quieres ser totalmente flexible:

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

Lo anterior solo aplica la forma del objeto ref w / currentproperty. Funcionará en todo momento para cualquier tipo de ref. Para el propósito de usar el tipo de apoyo, que es una forma de detectar cualquier inconsistencia cuando se desarrolla , esto probablemente sea suficiente. Parece muy poco probable que un objeto con forma { current: [any] }, y se pasa a un refToForwardaccesorio, no sea ​​una referencia real.

Sin embargo , es posible que desee declarar que su componente no espera ningún tipo de referencia, sino solo un cierto tipo, dado para qué necesita esa referencia.

He configurado una caja de arena que muestra algunas formas diferentes de declarar una referencia, incluso algunas no convencionales, y luego las probé con muchos tipos de accesorios. Puedes encontrarlo aquí .


Si solo espera una referencia que apunte a un elemento de entrada nativo, no a ningún HTML Element:

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

Si solo espera una referencia que apunte a un componente de la clase React:

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

Si solo espera una referencia que apunte a un componente funcional utilizando useImperativeHandlepara exponer algún método público:

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

Nota: el tipo de apoyo anterior es interesante porque también cubre los componentes react y los elementos DOM nativos, porque todos heredan el javascript base Object


No hay una única buena manera de declarar el tipo de prop para una referencia, depende del uso. Si su referencia apunta a algo muy identificado, puede valer la pena agregar un tipo de accesorio específico. De lo contrario, basta con comprobar la forma general.


Nota sobre la representación del lado del servidor

Si su código tiene que ejecutarse en el servidor, a menos que ya tenga un entorno DOM polyfill, Elemento cualquier otro tipo del lado del cliente estará undefineden NodeJS. Puede utilizar la siguiente cuña para apoyarlo:

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

Las siguientes páginas de React Docs brindan más información sobre cómo usar referencias, tanto object como callbacks , y también cómo usar useRefhook

Respuesta actualizada gracias a @Rahul Sagore, @Ferenk Kamra, @svnm y @Kamuela Franco

Pandaiolo
fuente
2
No Elementse define en la representación del lado del servidor. ¿Alguna solución a eso?
Rahul Sagore
Buen punto. ¿Qué pasa con esta calza? Element = typeof Element === 'undefined' ? function(){} : Element(Sugerida por Ryan Florence en algún número de github). Si funciona para usted, puedo agregarlo a mi respuesta.
Pandaiolo
Lo descubrí y agregué esto if (!isBrowser) { global.Element = null; }. Trabajó para mi. isBrowser = typeof window !== 'undefined';
Rahul Sagore
Gran respuesta. Me encantaría que TL; DR esté en la parte superior de la respuesta.
Kamuela Franco
12

Muy similar a la publicación de @ Pandaiolo,

PropTypes.elementType ahora ha sido agregado

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

Si PropTypes.elementTypese agregó PropTypes> = 15.7.0 el 10 de febrero de 2019 en este PR

svnm
fuente
Gracias @svnm, he actualizado mi respuesta para reflejar esto, ¡lo que lo hace más simple!
Pandaiolo
1
Al final, eso elementTypeno funcionó para mí, así que probé un montón de tipos de accesorios en diferentes tipos de componentes que pueden ser señalados por un árbitro, en una caja de arena. Aquí están los resultados: codesandbox.io/s/loving-benz-w6fbh . No estoy seguro de que cubrí todas las posibilidades, pero parece que el PropType correcta para el elemento DOM es instanceOf(Element)(o object), para una clase es instanceOf(Component)(o object) y para el caso de uso específico de un componente funcional utilizando useImperativeHandlees object. Pensamientos
Pandaiolo
9

Solo verifico la forma preferida y, dado que PropType.object no es muy valioso, terminé usando esto:

PropTypes.shape({ current: PropTypes.instanceOf(Element) })
Ferenc Kamras
fuente
1

Revisé todas las respuestas anteriores, pero recibo una advertencia de la reacción, así que investigué mucho y obtuve la respuesta correcta.

import React, { Component } from 'react';

ComponentName.propTypes = {
  reference: PropTypes.oneOfType([
    PropTypes.func,
    PropTypes.shape({ current: PropTypes.instanceOf(Component) }),
  ]),
};
Nisharg Shah
fuente