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