DEV Community

Altaskur
Altaskur

Posted on

React, Animando un Texto con useRef() desde la carga del componente

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.
Aquí está el efecto que capturó mi atención:

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.

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

},[]}
Enter fullscreen mode Exit fullscreen mode

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:

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:

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

},[]}
Enter fullscreen mode Exit fullscreen mode

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

Top comments (0)