Convert jQuery Animation to React Hook

Viewed 184

I am building an animation where the letters of two words appear one by one, similar to a slide-in effect. I have the code made with jQuery, but I need to implement it in my React app (built with hooks). The code that I have takes the text, splits it creating individual letters, and adds spans between those letters. This is the following code that I need to convert to React:

const logoText = document.querySelector('.logo');
const stringText = logoText.textContent;
const splitText = stringText.split("");

for (let i=0; i < splitText.length; i++) {
     text.innerHTML += "<span>" + splitText + "</span>" 
}

let char = 0;
let timer = setInterval(onTick, 50)

I was wondering if you guys could help me figure it out. Thanks a lot!

3 Answers

You need to iterate over the text and create a timeout function for every letter with a different time of execution, that way will be visible the slide effect you are expecting:

Custom hook

const useSlideInText = text => {
  const [slide, setSlide] = useState([]);
  useEffect(() => {
    Array.from(text).forEach((char, index) => {
      const timeout = setTimeout(
        () =>
          setSlide(prev => (
            <>
              {prev}
              <span>{char}</span>
            </>
          )),
        index * 100
      );
    });
  }, []);

  return slide;
};

Usage

 function App() {
    const slide = useSlideInText("hello");
    return (
      <div>
        {slide}
      </div>
    );
 }

Working example

I am assuming the React components that you want to run this hook in possess the text you want to split. I am also assuming that on the interval, you want to reveal more of the text. In that case my example solution would look like this:

Hook

import {useState, useEffect} from "react";

const useSlideInText = (text) => {
    const [revealed, setRevealed] = useState(0);

    useEffect(() => {
        if (revealed < text.length) {
            setTimeout(() => setRevealed(revealed + 1), 50);
        }
    });
    
    return text.split('').slice(0, revealed).map((char) => (<span>{char}</span>));
}

Example usage

const MyComponent = (props) => {
    const displayText = useSlideInText(props.text);

    return <div>{displayText}</div>;
};

going off of the other answer:

const generateDisplayTest = (text, numChars) => text.split('').slice(0, numChars).map((char) => (<span>{char}</span>));

const MyComponent = (props) => {
    const [revealed, setRevealed] = useState(0);

    useEffect(() => {
        if (revealed < props.text.length) {
            setTimeout(() => setRevealed(revealed + 1), 50);
        }
    }, [revealed]);
    const displayText = generateDisplayTest(props.text, revealed);

    return <div>{displayText}</div>;
};

including [revealed] in the useEffect means that useEffect will run every time that revealed changes. Also I always feel that useState/useEffect should live on the component, it has been that way in the place I worked but I'm not sure if that is industry standard.

Related