v19.2Latest

Referenciar valores con Refs

Cuando quieras que un componente "recuerde" cierta información, pero no quieras que esa informacióndesencadene nuevos renderizados, puedes usar unaref.

Aprenderás
  • Cómo añadir una ref a tu componente
  • Cómo actualizar el valor de una ref
  • En qué se diferencian las refs del estado
  • Cómo usar las refs de manera segura

Añadir una ref a tu componente

Puedes añadir una ref a tu componente importando el HookuseRefdesde React:

Dentro de tu componente, llama al HookuseRefy pasa el valor inicial al que quieres hacer referencia como único argumento. Por ejemplo, aquí tienes una ref al valor0:

useRefdevuelve un objeto como este:

Una flecha con la palabra 'current' escrita en ella metida en un bolsillo con la palabra 'ref' escrita.

Ilustrado porRachel Lee Nabors

Puedes acceder al valor actual de esa ref a través de la propiedadref.current. Este valor es intencionalmente mutable, lo que significa que puedes leerlo y escribirlo. Es como un bolsillo secreto de tu componente que React no rastrea. (¡Esto es lo que lo convierte en una "vía de escape" del flujo de datos unidireccional de React; más sobre eso más adelante!)

Aquí, un botón incrementaráref.currenten cada clic:

La ref apunta a un número, pero, al igual que elestado, podrías apuntar a cualquier cosa: una cadena, un objeto o incluso una función. A diferencia del estado, la ref es un objeto JavaScript plano con la propiedadcurrentque puedes leer y modificar.

Ten en cuenta queel componente no se vuelve a renderizar con cada incremento.Al igual que el estado, las refs son conservadas por React entre renderizados. Sin embargo, establecer el estado vuelve a renderizar un componente. ¡Cambiar una ref no lo hace!

Ejemplo: construir un cronómetro

Puedes combinar refs y estado en un solo componente. Por ejemplo, hagamos un cronómetro que el usuario pueda iniciar o detener presionando un botón. Para mostrar cuánto tiempo ha pasado desde que el usuario presionó "Iniciar", necesitarás llevar un registro de cuándo se presionó el botón de Inicio y cuál es la hora actual.Esta información se usa para renderizar, así que la mantendrás en el estado:

Cuando el usuario presione "Iniciar", usarássetIntervalpara actualizar la hora cada 10 milisegundos:

Cuando se presione el botón "Detener", necesitarás cancelar el intervalo existente para que deje de actualizar la variable de estadonow. Puedes hacer esto llamando aclearInterval, pero necesitas darle el ID del intervalo que fue devuelto previamente por la llamada asetIntervalcuando el usuario presionó Inicio. Necesitas guardar el ID del intervalo en algún lugar.Como el ID del intervalo no se usa para renderizar, puedes guardarlo en una ref:

Cuando una pieza de información se usa para renderizar, mantenla en el estado. Cuando una pieza de información solo la necesitan los manejadores de eventos y cambiarla no requiere un nuevo renderizado, usar una referencia (ref) puede ser más eficiente.

Diferencias entre referencias y estado

Quizás pienses que las referencias parecen menos "estrictas" que el estado—puedes mutarlas en lugar de tener que usar siempre una función de actualización de estado, por ejemplo. Pero en la mayoría de los casos, querrás usar estado. Las referencias son una "salida de emergencia" que no necesitarás a menudo. Aquí se compara el estado y las referencias:

referencias (refs)estado (state)
useRef(initialValue)devuelve{ current: initialValue }useState(initialValue)devuelve el valor actual de una variable de estado y una función establecedora de estado ([value, setValue])
No provoca un nuevo renderizado cuando lo cambias.Provoca un nuevo renderizado cuando lo cambias.
Mutable—puedes modificar y actualizar el valor decurrentfuera del proceso de renderizado."Inmutable"—debes usar la función establecedora de estado para modificar las variables de estado y encolar un nuevo renderizado.
No deberías leer (ni escribir) el valorcurrentdurante el renderizado.Puedes leer el estado en cualquier momento. Sin embargo, cada renderizado tiene su propiainstantáneadel estado que no cambia.

Aquí hay un botón contador que se implementa con estado:

Como el valor decountse muestra, tiene sentido usar un valor de estado para él. Cuando el valor del contador se establece consetCount(), React vuelve a renderizar el componente y la pantalla se actualiza para reflejar el nuevo conteo.

Si intentaras implementar esto con una referencia, React nunca volvería a renderizar el componente, ¡por lo que nunca verías cambiar el conteo! Observa cómo hacer clic en este botónno actualiza su texto:

Esta es la razón por la que leerref.currentdurante el renderizado conduce a un código poco fiable. Si necesitas eso, usa estado en su lugar.

Deep Dive
¿Cómo funciona useRef internamente?

Cuándo usar referencias

Típicamente, usarás una referencia cuando tu componente necesite "salirse" de React y comunicarse con APIs externas, a menudo una API del navegador que no afecte la apariencia del componente. Aquí hay algunas de estas situaciones poco comunes:

Si tu componente necesita almacenar algún valor, pero no afecta la lógica de renderizado, elige referencias.

Mejores prácticas para referencias

Seguir estos principios hará que tus componentes sean más predecibles:

  • Trata las referencias como una vía de escape.Las referencias son útiles cuando trabajas con sistemas externos o APIs del navegador. Si gran parte de la lógica de tu aplicación y el flujo de datos dependen de referencias, quizás quieras reconsiderar tu enfoque.
  • No leas ni escribasref.currentdurante el renderizado.Si necesitas alguna información durante el renderizado, usaestadoen su lugar. Dado que React no sabe cuándo cambiaref.current, incluso leerlo durante el renderizado hace que el comportamiento de tu componente sea difícil de predecir. (La única excepción a esto es código comoif (!ref.current) ref.current = new Thing(), que solo establece la referencia una vez durante el primer renderizado).

Las limitaciones del estado de React no se aplican a las referencias. Por ejemplo, el estado actúa como unainstantánea para cada renderizado y no se actualiza sincrónicamente.Pero cuando mutas el valor actual de una referencia, cambia inmediatamente:

Esto se debe a quela referencia en sí es un objeto JavaScript normal,y por lo tanto se comporta como tal.

Tampoco necesitas preocuparte porevitar la mutacióncuando trabajas con una referencia. Mientras el objeto que estás mutando no se use para el renderizado, a React no le importa lo que hagas con la referencia o su contenido.

Referencias y el DOM

Puedes apuntar una referencia a cualquier valor. Sin embargo, el caso de uso más común para una referencia es acceder a un elemento del DOM. Por ejemplo, esto es útil si quieres enfocar un campo de entrada de manera programática. Cuando pasas una referencia a un atributorefen JSX, como<div ref={myRef}>, React colocará el elemento del DOM correspondiente enmyRef.current. Una vez que el elemento sea eliminado del DOM, React actualizarámyRef.currentpara que seanull. Puedes leer más sobre esto enManipulando el DOM con Referencias.

Resumen

  • Las refs son una vía de escape para mantener valores que no se utilizan para el renderizado. No las necesitarás con frecuencia.
  • Una ref es un objeto JavaScript simple con una única propiedad llamadacurrent, que puedes leer o establecer.
  • Puedes pedirle a React que te dé una ref llamando al HookuseRef.
  • Al igual que el estado, las refs te permiten retener información entre re-renderizaciones de un componente.
  • A diferencia del estado, establecer el valor decurrentde la ref no desencadena una re-renderización.
  • No leas ni escribasref.currentdurante el renderizado. Esto hace que tu componente sea difícil de predecir.

Prueba algunos desafíos

Challenge 1 of 4:Repara un chat con entrada rota #

Escribe un mensaje y haz clic en “Enviar”. Notarás que hay un retraso de tres segundos antes de que veas la alerta “¡Enviado!”. Durante este retraso, puedes ver un botón “Deshacer”. Haz clic en él. Este botón “Deshacer” está destinado a evitar que aparezca el mensaje “¡Enviado!”. Lo hace llamando a clearTimeout para el ID del timeout guardado durante handleSend. Sin embargo, incluso después de hacer clic en “Deshacer”, el mensaje “¡Enviado!” sigue apareciendo. Encuentra por qué no funciona y arréglalo.