Cómo comenzar rápidamente con VUE3.0: ingrese y aprenda
En los proyectos de React, hay muchos escenarios donde se necesita Ref
. Por ejemplo, use ref
para obtener el nodo DOM y obtener la instancia del objeto ClassComponent; use useRef
Hook para crear un objeto Ref para resolver el problema de que setInterval
no pueda obtener el último estado; también puede llamar a React.createRef
Método para crear manualmente un objeto Ref
.
Aunque Ref
es muy simple de usar, todavía es inevitable encontrar problemas en proyectos reales. Este artículo resolverá varios problemas relacionados con Ref
desde la perspectiva del código fuente y aclarará qué se hace detrás ref
. Después de leer este artículo, es posible que tenga una comprensión más profunda de Ref
.
en primer lugar, ref
es la abreviatura de reference
, que es una referencia. En el archivo de declaración de tipo de react
, puede encontrar varios tipos relacionados con Ref, y se enumeran aquí.
RefObject<T> {solo lectura actual: T nulo; interfaz MutableRefObject<T> { current: T }
Cuando se usa useRef
Hook, se devuelve RefObject/MutableRefObejct. Ambos tipos definen una estructura de objeto de { current: T }
. La diferencia es que la propiedad actual de RefObject
es de solo lectura. , Typecript advertirá ⚠️ si se modifica refObject.current
.
constante ref = useRef<cadena>(nulo) ref.current = '' // Error
TS: No se puede asignar a "actual" porque es una propiedad de solo lectura.
Mire la definición del método useRef
. Aquí se utiliza la sobrecarga de funciones . Cuando el parámetro genérico entrante T
no contiene null
, se devuelve RefObject<T>
. Cuando contiene null
, se devuelve MutableRefObject<T>
.
función useRef<T>(initialValue: T): MutableRefObject<T>; function useRef<T>(initialValue: T | null): RefObject<T>
Entonces, si desea que la propiedad actual del objeto de referencia creado sea modificable, debe agregar | null
.
constante ref = useRef<cadena | nulo>(nulo) ref.current = '' // OK,
al llamar al método React.createRef()
, también se devuelve un RefObject
.
createRef
createRef(): RefObject { objetoref constante = { actual: nulo, }; si (__DEV__) { Objeto.seal(refObject); } devolver objeto ref; }
RefObject/MutableRefObject
se agregó en la versión 16.3
. Si usa versiones anteriores, debe usar Ref Callback
.
Usar Ref Callback
es pasar una función de devolución de llamada. Cuando React devuelve la llamada, se devuelve la instancia correspondiente y se puede guardar sola para llamar. El tipo de esta función de devolución de llamada es RefCallback
.
escriba RefCallback<T> = (instancia: T | null) => void
Ejemplo de uso de RefCallback
:
importar React desde 'react'; clase de exportación CustomTextInput extiende React.Component { entrada de texto: HTMLInputElement | nulo = nulo; saveInputRef = (elemento: HTMLInputElement | nulo) => { this.textInput = elemento; } prestar() { devolver ( <tipo de entrada="texto" ref={this.saveInputRef} /> ); } }
En la declaración de tipo, también hay tipos Ref/LegacyRef, que se utilizan para referirse al tipo Ref en general. LegacyRef
es una versión compatible. En la versión anterior, ref
también podría ser una cadena.
escriba Ref<T> = RefCallback<T> | RefObject<T> | escriba LegacyRef<T> = string | Ref<T>;
solo comprendiendo los tipos relacionados con Ref podrá sentirse más cómodo escribiendo Typecript.
PasarCuando usamos ref
en un componente JSX, configuramos una Ref
para el atributo ref
. Todos sabemos que la sintaxis de jsx
se compilará en forma de createElement
mediante herramientas como Babel.
//jsx <Aplicación ref={ref} id="mi-aplicación" >>Aplicación> // compilado en React.createElement(Aplicación, { referencia: referencia, id: "mi-aplicación" });
Parece que ref
no es diferente de otros accesorios, pero si intenta imprimir props.ref dentro del componente, no está undefined
. Y la consola del entorno dev
le dará indicaciones.
Al intentar acceder a él, se devolverá un valor
undefined
. Si necesita acceder al mismo valor dentro del componente secundario, debe pasarlo como un accesorio diferente.
¿Qué hace React con ref? Como puede ver en el código fuente de ReactElement, ref
es RESERVED_PROPS
. key
también tienen este tratamiento. Se procesarán y extraerán especialmente de los accesorios y se pasarán a Element
.
constante RESERVED_PROPS = { clave: verdadero, referencia: verdadero, __yo: cierto, __fuente: verdadero, };
Entonces ref
es “props“
que será tratado de manera especial.
Antes de la versión 16.8.0
, el componente de función no tenía estado y solo se representaba en función de los accesorios entrantes. Con Hook, no solo puede tener un estado interno, sino también exponer métodos para llamadas externas (lo que requiere forwardRef
y useImperativeHandle
).
Si usa ref
directamente para un Function Component
, la consola en el entorno de desarrollo le advertirá que debe empaquetarlo con forwardRef
.
entrada de función () { devolver <entrada /> } referencia constante = usarRef() <Input ref={ref} />
Los componentes de la función no pueden recibir referencias. Los intentos de acceder a esta referencia fallarán. ¿Quiso utilizar React.forwardRef()
forwardRef
Ver el código fuente ReactForwardRef.js. Doble el código relacionado con __DEV__
. Es solo un componente de alto orden extremadamente simple. Reciba un componente de función renderizado, envuélvalo y defina $$typeof
como REACT_FORWARD_REF_TYPE
y return
.
Rastree el código y busque resolveLazyComponentTag, donde $$typeof
se analizará en la WorkTag correspondiente.
La WorkTag correspondiente a REACT_FORWARD_REF_TYPE
es ForwardRef. Entonces ForwardRef ingresará a la lógica de updateForwardRef.
caso Referencia Adelante: { niño = actualizarForwardRef( nulo, trabajo en progreso, Componente, accesorios resueltos, rendercarriles, ); devolver niño; }
Este método llamará al método renderWithHooks y pasará ref
en el quinto parámetro.
nextChildren = renderWithHooks( actual, trabajo en progreso, prestar, siguiente accesorios, ref, // aquí renderLanes, );
Continúe rastreando el código e ingrese el método renderWithHooks. Puede ver que se pasará ref
como el segundo parámetro de Component
. En este punto podemos entender de dónde viene el segundo parámetro ref
de FuncitonComponent
envuelto por forwardRef
(en comparación con el segundo parámetro del constructor ClassComponent que es Context).
Sabiendo cómo pasar la referencia, la siguiente pregunta es cómo se asigna la referencia.
El(asigne un RefCallback a ref y rompa el punto en la devolución de llamada). Rastree el código commitAttachRef. En este método, se juzgará si la referencia del nodo Fiber es function
o RefObject y la instancia. serán procesados según el tipo. Si el nodo Fiber es un HostComponent ( tag = 5
), que es un nodo DOM, la instancia es el nodo DOM y si el nodo Fiber es un ClassComponent ( tag = 1
), la instancia es la instancia del objeto;
función commitAttachRef (trabajo terminado) { var ref = trabajo terminado.ref; si (ref! == nulo) { var instanciaToUse = trabajo terminado.stateNode; if (tipo de referencia === 'función') { ref(instanciaParaUsar); } demás { ref.current = instanciaToUse; } } }
Lo anterior es la lógica de asignación de ref en HostComponent y ClassComponent. Para los componentes de tipo ForwardRef, se utilizan códigos diferentes, pero el comportamiento es básicamente el mismo. Puede ver el imperativoHandleEffect aquí.
A continuación, continuamos profundizando en el código fuente de React para ver cómo se implementa useRef.
localiza el código de tiempo de ejecución de useRef ReactFiberHooks mediante el seguimiento del código
Aquí hay dos métodos, mountRef
y updateRef
. Como sugiere el nombre, corresponden a las operaciones en ref
cuando Fiber
mount
y update
.
función actualizarRef<T>(valorinicial: T): {|actual: T|} { gancho constante = updateWorkInProgressHook(); devolver gancho.memoizedState; } función mountRef<T>(valorinicial: T): {|actual: T|} { gancho constante = mountWorkInProgressHook(); constante ref = {actual: valor inicial}; gancho.memoizedState = ref; devolver referencia; }
Puedes ver que cuando mount
, useRef
crea un RefObject
y lo asigna al memoizedState
del hook
. Cuando update
, se saca y se devuelve directamente.
Diferentes Hook memoizedState guardan diferentes contenidos. useState
guarda información state
, useEffect
guarda objetos effect
, useRef
guarda objetos ref
...
mountWorkInProgressHook
y updateWorkInProgressHook
están respaldados por una lista vinculada de Hooks. Cuando la lista vinculada no se modifica, a continuación, puede hacerlo. recupera el mismo objeto memoizedState cada vez que renderizas useRef. Es así de simple.
En este punto, entendemos la lógica de pasar y asignar ref
en React, así como el código fuente relacionado con useRef
. Utilice una pregunta de aplicación para consolidar los puntos de conocimiento anteriores: hay un componente de entrada dentro del componente, y el elemento externo HTMLInputElement
debe usarse para acceder al nodo DOM
. para implementarlo?
entrada constante = forwardRef((props, ref) => { const internalRef = useRef<HTMLInputElement>(nulo) devolver ( <entrada {...props} ref={???} /> ) })
Considere cómo debería escribirse ???
en el código anterior.
============ Línea divisoria de respuesta ==============
Al comprender la implementación interna relacionada con Ref, es obvio que podemos crear un RefCallback
aquí, que puede manejar múltiples Simplemente asigne una ref
.
función de exportación combineRefs<T = cualquiera>( referencias: Array<MutableRefObject<T | null> | RefCallback<T>> ): Reaccionar.RefCallback<T> { valor de retorno => { referencias.forEach(ref => { if (tipo de referencia === 'función') { ref(valor); } si no (ref! == nulo) { ref.actual = valor; } }); }; } entrada constante = forwardRef((props, ref) => { const internalRef = useRef<HTMLInputElement>(nulo) devolver ( <entrada {...props} ref={combineRefs(ref, internalRef)} /> ) })