Manipular el DOM con Refs
Obtener una ref del nodo
Para acceder a un nodo DOM gestionado por React, primero importa el Hook useRef
:
Luego, úsalo para declarar una ref dentro de tu componente
Finalmente, pasa la ref como el atributo ref
a la etiqueta JSX en el que quieres obtener el nodo DOM:
El Hook useRef
devuelve un objeto con una sola propiedad llamada current
. Inicialmente, myRef.current
va a ser null
. Cuando React cree un nodo DOM para este <div>
, React pondrá una referencia a este nodo en myRef.current
. Entonces podrás acceder a este nodo DOM desde tus y usar las integradas definidas en él.
Ejemplo: Enfocar el campo de texto input
En este ejemplo, hacer clic en el botón va a enfocar el input:
Para implementar esto:
- Declara
inputRef
con el HookuseRef
. - Pásalo como
<input ref={inputRef}>
. Esto le dice a React que coloque el nodo DOM<input>
eninputRef.current
. - En la función
handleClick
, lee el nodo input del DOM desdeinputRef.current
y llama a en él coninputRef.current.focus()
. - Pasa el controlador de evento
handleClick
a<button>
cononClick
.
Mientras manipular el DOM es el caso de uso más común para las refs, el Hook useRef
puede ser usado para almacenar otras cosas fuera de React, como las ID de temporizadores. De manera similar al estado, las refs permanecen entre renderizados. Las refs son como variables de estado que no desencadenan nuevos renderizados cuando las pones. Lee acerca de las refs en
Ejemplo: Desplazarse a un elemento
Puedes tener más de una ref en un componente. En este ejemplo, hay un carrusel de tres imágenes. Cada botón centra una imagen al llamar al método del navegador en el nodo DOM correspondiente:
Profundizar
Cómo gestionar una lista de refs usando un callback ref
Cómo gestionar una lista de refs usando un callback ref
En los ejemplos de arriba, hay un número predefinido de refs. Sin embargo, algunas veces es posible que necesites una ref en cada elemento de la lista, y no sabes cuantos vas a tener. Algo como esto no va a funcionar:
Esto es porque los Hooks solo tienen que ser llamados en el nivel más alto de tu componente. No puedes llamar a useRef
en un bucle, en una condición, o dentro de una llamada a map()
.
Una posible forma de evitar esto es hacer una sola ref a su elemento padre, y luego usar métodos de manipulación del DOM como para “encontrar” los nodos hijos individuales a partir de él. Sin embargo, esto es frágil y puede romperse si la estructura del DOM cambia.
Otra solución es pasar una función al atributo ref
. A esto se le llama un React llamará tu callback ref con el nodo DOM cuando sea el momento de poner la ref, y con null
cuando sea el momento de limpiarla. Esto te permite mantener tu propio array o un , y acceder a cualquier ref por su índice o algún tipo de ID.
Este ejemplo te muestra cómo puedes usar este enfoque para desplazarte a un nodo arbitrario en una lista larga:
En este ejemplo, itemsRef
no contiene un solo nodo DOM. En su lugar, contiene un desde el ID del elemento hasta un nodo DOM. () El en cada elemento de la lista se encarga de actualizar el Map:
This lets you read individual DOM nodes from the Map later.
Accediendo a nodos DOM de otros componentes
Puedes pasar refs desde un componente padre a componentes hijos .
En el ejemplo de arriba, se crea una ref en el componente padre, MyForm
, y se pasa al componente hijo, MyInput
. MyInput
luego pasa la ref a <input>
. Debido a que <input>
es un React establece la propiedad .current
de la ref al elemento DOM <input>
.
La inputRef
creada en MyForm
ahora apunta al elemento DOM <input>
devuelto por MyInput
. Un manejador de clics creado en MyForm
puede acceder a inputRef
y llamar a focus()
para establecer el foco en <input>
.
Profundizar
Exponiendo un subconjunto de la API con un manejador imperativo
Exponiendo un subconjunto de la API con un manejador imperativo
En el ejemplo de arriba, la ref que se pasa MyInput
se pasa al elemento input del DOM original. Esto le permite al componente padre llamar a focus()
en él. Sin embargo, esto también le permite al componente padre hacer otra cosa, por ejemplo, cambiar sus estilos CSS. En casos pocos comunes, quizás quieras restringir la funcionalidad expuesta. Puedes hacerlo con :
Aquí, realInputRef
dentro de MyInput
mantiene el nodo DOM de input actual. Sin embargo, indica a React a proveer tu propio objeto especial como el valor de una ref al componente padre. Por lo tanto, inputRef.current
dentro del componente Form
solo va a tener el método focus
. En este caso, el “manejador” ref no es el nodo DOM, sino el objeto personalizado que creaste dentro de la llamada de .
Cuando React adjunta las refs
En React, cada actualización está dividida en :
- Durante el renderizado, React llama a tus componentes para averiguar que debería estar en la pantalla.
- Durante la confirmación, React aplica los cambios a el DOM.
En general, acceder a las refs durante el renderizado. Eso va también para las refs que tienen nodos DOM. Durante el primer renderizado, los nodos DOM aún no han sido creados, entonces ref.current
será null
. Y durante el renderizado de actualizaciones, los nodos DOM aún no se han actualizado. Es muy temprano para leerlos.
React establece ref.current
durante la confirmación. Antes de actualizar el DOM, React establece los valores afectados de ref.current
a null
. Después de actualizar el DOM, React inmediatamente los establece en los nodos DOM correspondientes.
Generalmente, vas a acceder a las refs desde los controladores de eventos. Si quieres hacer algo con una ref, pero no hay un evento en particular para hacerlo, es posible que necesites un Efecto. Discutiremos los Efectos en las próximas páginas.
Profundizar
Vaciando actualizaciones de estado sincrónicamente con flushSync
Vaciando actualizaciones de estado sincrónicamente con flushSync
Considere un código como el siguiente, que agrega un nuevo todo y desplaza la pantalla hasta el último hijo de la lista. Observa cómo, por alguna razón, siempre se desplaza hacia el todo que estaba justo antes del último que se ha agregado.
El problema está con estas dos lineas:
En React, Generalmente, esto es lo que quieres. Sin embargo, aquí causa un problema porque setTodos
no actualiza el DOM inmediatamente. Entonces, en el momento en el que desplazas la lista al último elemento, el todo aún no ha sido agregado. Esta es la razón por la que al desplazarse siempre se “retrasa” en un elemento.
Para arreglar este problema, puedes forzar a React a actualizar (“flush”) el DOM sincrónicamente. Para hacer esto, importa flushSync
del react-dom
y envuelve el actualizador de estado en una llamada a flushSync
:
Esto le indicará a React que actualice el DOM sincrónicamente justo después que el código envuelto en flushSync
se ejecute. Como resultado, el último todo ya estará en el DOM en el momento que intentes desplazarte hacia él.
Mejores prácticas para la manipulación del DOM con refs
Las refs son una vía de escape. Sólo deberías usarlas cuando tengas que “salirte de React”. Ejemplos comunes de esto incluyen la gestión del foco, la posición del scroll, o una llamada a las API del navegador que React no expone.
Si te limitas a acciones no destructivas como enfocar o desplazarte, no deberías encontrar ningún problema. Sin embargo, si intentas modificar el DOM manualmente, puedes arriesgarte a entrar en conflicto con los cambios que React está haciendo.
Para ilustrar este problema, este ejemplo incluye un mensaje de bienvenida y dos botones. El primer botón alterna su presencia usando y , como normalmente lo harías en React. El segundo botón usa la para eliminarlo forzadamente del DOM fuera del control de React.
Intenta presionar “Alternar con setState” unas cuantas veces. El mensaje debe desaparecer y aparecer otra vez. Luego presiona “Eliminar del DOM”. Esto lo eliminará forzadamente. Finalmente, presiona “Alternar con setState”:
Después de que hayas eliminado el elemento DOM, intentar usar setState
para mostrarlo de nuevo provocará un fallo. Esto se debe a que has cambiado el DOM, y React no sabe cómo seguir gestionándolo correctamente.
Evita cambiar nodos DOM gestionados por React. Modificar, agregar hijos, o eliminar hijos de elementos que son gestionados por React pueden traer resultados inconsistentes visuales o fallos como el de arriba.
Sin embargo, esto no quiere decir que no puedas en absoluto. Requiere de cuidado. Puedes modificar de manera segura partes del DOM que React no tenga motivos para actualizar. Por ejemplo, si algún <div>
siempre está vacío en el JSX, React no tendrá un motivo para tocar su lista de elementos hijos. Por lo tanto, es seguro agregar o eliminar manualmente elementos allí.
Recapitulación
- Las refs son un concepto genérico, pero a menudo las vas a usar para almacenar elementos del DOM.
- Tú le indicas a React a poner un nodo DOM dentro de
myRef.current
pasándole<div ref={myRef}>
. - Normalmente, vas a usar las refs para acciones no destructivas como enfocar, desplazar, o medir elementos DOM.
- Un componente no expone sus nodos DOM por defecto. Puedes optar por exponer un nodo DOM usando
forwardRef
y pasando el segundo argumentoref
a un nodo específico. - Evita cambiar nodos DOM gestionados por React.
- Si modificas nodos DOM gestionados por React, modifica las partes en donde React no tenga motivos para actualizar.