Has comenzado valientemente a implementar TS en tu código, ya no creas archivos .jsx ahora son .tsx, te sientes algo inseguro pero entusiasmado a la vez y ¡todo va bien!
tus componentes y la declaración de tus props se ven muy pro y la linterna te ayuda en tu día a día como developer, incluso si eres suficientemente observador(a) ya notaste que ya no pasas tantas horas buscando un error desconocido y que logras más en menos tiempo
. Sip Typescript está chingón. Hasta que... (música de "Stranger Things" y suspenso)
Resulta que te aparecen errores cuando usas algunos hooks
que requieren unos tipos muy específicos como por ejemplo cuando utilizas forwardRef
para pasar un ref como prop para usar la api de un nodo HTML; cómo cuándo quieres que el sitio web haga scroll hasta cierto elemento en la página.
¿Cómo declaramos correctamente los tipos para useRef?
En esta entrada voy a compartirte cómo declaro mis tipos cuando trabajo con useRef y forwardRef, con 3 sencillas reglas
Siempre es bueno ser muy específico con los nodos que se almacenarán en una referencia, mientras más acotadas sean las opciones es mejor, así evitamos que nuestro programa tenga demasiadas opciones para fallar
, en mi ejemplo voy a imaginar que tengo un par de secciones en mi página y cuando selecciono una de esas secciones en un menú, el sitio web hace scroll hasta llegar a dicha sección. Simple. Con eso en mente declaremos nuestro componente contenedor:
1const Container = () => { 2 const nosotrosRef = useRef<HTMLDivElement>() 3 const preciosRef = useRef<HTMLDivElement>() 4 const contactoRef = useRef<HTMLDivElement>() 5 return ( 6 <div> 7 <Section id="nosotros" ref={nosotrosRef} /> 8 <Section id="precios" ref={preciosRef} /> 9 <Section id="contacto" ref={contactoRef} /> 10 </div> 11 ) 12} 13
En este caso estamos seguros que la referencia contendrá un nodo HTMLDivElement
pero dependiendo de tu app podría ser cualquier nodo o incluso un grupo de ellos <HTMLDivElement | HTMLFormElement>
Incluso si aún no estás segura de cual usarás puedes quedarte un nivel arriba HTMLElement
para después actualizar.
Aquí también por buena práctica vamos a inicializar nuestra referencia con null
lo que nos ahorrará algunos otros errores que describiré en el siguiente punto y que no queremos ver.
1 const nosotrosRef = useRef<HTMLDivElement>(null) 2 const preciosRef = useRef<HTMLDivElement>(null) 3 const contactoRef = useRef<HTMLDivElement>(null) 4
Donde sea que veas este warning, donde el protagonista es nuestro viejo conocido undefined
es necesario deshacernos de él, porque si no, nos querrá presentar a su compa el unknown
.
Para resolver esto sólo tenemos que declarar nuestra referencia en nuestro componente Section con el mismo tipo de dato que se le ha pasado desde el padre, y para ello tenemos una herramienta de React que se llama: RefObject
y podemos usarla así:
1const Section = forwardRef(( 2{ id }: { id: string }, 3ref:RefObject<HTMLDivElement> 4) => ( 5 <div ref={ref}> 6 <h2>Sección {id}</h2> 7 </div> 8)); 9
Aquí estamos diciendo que ref recibirá un objecto ref ({current:Node}
) con un nodo div dentro de current.
Gracias React team.
Una referencia por naturaleza está pensada para guardar un valor que no cambia con el tiempo a través de diferentes renders, y que en teoría debería almacenar un valor constante, pero su uso se ha extendido y ahora lo usamos también para guardar datos dinámicos pero que no necesitan detonar renders del componente
, y es importante decirle a Typescript que mutaremos el valor de este ref
aunque la manera de informarselo no es obvia...
Vamos a decir que por alguna razón muy creativa nuestra referencia va a cambiar de almacenar un div a estar vacía (o darle algún otro nodo como un form o un h2)
, dependiendo de la acción del usuario.
En la función que actualiza la referencia tendremos un error así:
La forma más moderna de resolver este error es declarar los tipos del forwardRef y tolerar el null
así que cambiamos la forma de declarar nuestros tipos de la siguiente manera:
1type SectionProps = { 2 id: string; 3}; 4 5const Section = 6forwardRef<HTMLDivElement | null, SectionProps> 7(({ id }, ref) => { 8 const toggle = () => { 9 ref.current = null; 10}; 11 return ( 12 <div ref={ref}> 13 <h2>Sección {id}</h2> 14 </div> 15 ); 16}); 17
De esta forma toleramos no sólo que ref pueda mutar a null (o algún otro tipo) también toleramos la mutación en sí misma, es decir la escritura en ref.current
Es importante decir que el orden de la declaración en el forwardRef es importante, primero declara el tipo del ref y luego el de los props.
¡Y ya está! considera estas 3 simples reglas cada que declares referencias y no olvides darte una revisada a la lista de tipos HTML que existen para que no te sorprenda useRef
nunca más, te dejo unos links interesantes aquí abajo.
Si quieres aprender a programar, también puedes ir directo a mi curso gratis
en fixtergeek.com o suscríbete a mi lista de correo para que no te pierdas mi contenido sobre TS.
Además de que estoy por lanzar un curso de Typescript para principiantes en las próximas semanas
, ¡entérate del lanzamiento suscribiéndote!
Sin más, gracias por tu tiempo. Te mando un abrazo.
Happy coding <3
Bliss.