React, Animando un Texto con useRef() desde la carga del componente
Tags: react, javascript, beginners, spanish
Published: 2024-02-16 16:06:40

Recientemente, quise añadir un toque animado a mi proyecto final en React, inspirado por una animación compartida por la creadora de contenido [Mewtru](https://bento.me/mewtru). 
Aquí está el efecto que capturó mi atención:

{% codepen https://codepen.io/altaskur/pen/VwRXoay %}

La idea inicial era trasladarlo de JavaScript a React, y automáticamente pensé en utilizar `useRef` para lograrlo. Sin embargo, como es común en el mundo del desarrollo, las cosas no siempre siguen el guion previsto, y los desafíos surgieron justo al cargar el componente.

## Problema al cargar el Componente

La sorpresa y el motivo de este post, llegó al momento de cargar el componente, la consola lanzaba el mismo error.

> "append is not defined divElement is null"

En un principio, sospeché de algún error de sintaxis, Sin embargo tras revisar el código y revisarlo repetidamente, el problema se mantenía con el mensaje `is null`. 

te dejo parte del código para que veas como estaba todo definido.

```jsx
const div1 = useRef(null);
const div2 = useRef(null);

useEffect( () =>{
   const words = "@property - example";

   const ANIMATION_DURATION = 4000;

   const characters = words.split("").forEach((character, index) => {
      const createElement = (offset) => {
      const div = document.createElement("div");
      div.innerText = character;
      div.classList.add("character");
      div.style.animationDelay = `-${index * (ANIMATION_DURATION / 16) - offset
      }ms`;
      return div;
  };

  div1.current.append(createElement(0));
  div2.current.append(createElement(-1 * (ANIMATION_DURATION / 2)));
});

},[]}
```

Esta es la raíz del problema, div1 y div2 son `null` siempre.

## La incógnita

Mi primera suposición fue que quizás no le estaba dando tiempo al hook de "vincularse" con el contenido. Sin embargo, el contenido de useEffect() se ejecuta al inicio del componente después de que todo se ha cargado, según la [documentación oficial de React](https://react.dev/reference/react/useEffect#caveats):

> React will generally let the browser paint the updated screen first before running your Effect.

A pesar de esto, el problema persistía, y el mensaje `null` seguía apareciendo. La confusión se apoderaba de mí: ¿por qué sigue siendo null?

## Solución

Después de explorar múltiples fuentes en la web y  consultar a distintas inteligencias artificiales sin obtener resultados significativos, finalmente encontré una solución:

En el `useEffect()` forzar a comprobar si los `useRef()` están definidos, en ese caso ejecutar el código. Te dejo el código completo:

```jsx
useEffect( () =>{

    if(div1 && div2){
       const words = "@property - example";

       const ANIMATION_DURATION = 4000;

       const characters = words.split("").forEach((character, index) => {
          const createElement = (offset) => {
              const div = document.createElement("div");
              div.innerText = character;
              div.classList.add("character");
              div.style.animationDelay = `-${index * (ANIMATION_DURATION / 16) - offset}ms`;
              return div;
          };

          div1.current.append(createElement(0));
          div2.current.append(createElement(-1 * (ANIMATION_DURATION / 2)));
        }
    });

},[]}
```

## Resolución

¡Y funcionó! 🎉🎉 el componente cargaba correctamente, y la animación entraba. Aún no consigo entender porque hay que forzar al `useEffect()` a que se definan los `useState()` si este se ejecuta después de renderizar todo. además de como consigue esa reactividad, si el `useEffect()` está definido para que se ejecute sólo al montar el componente.

> ¿Te ha pasado alguna vez, o sabes lo que está ocurriendo? no dudes en dejarlo en comentarios.

## Referencias

- [Mewtru](https://bento.me/mewtru).
- [CodePen](https://codepen.io/altaskur/pen/VwRXoay).
- [Documentación oficial de React](https://react.dev/reference/react/useEffect#caveats).