React Typewriter Effect Missing Characters Issue on Text Change

105 Views Asked by At

enter image description hereenter image description here
While implementing a custom typewriter effect in a React application using a hook, there is an issue where the letter 'a' (the second alphabet) is missing when displaying the text 'tarot card with intricate detailed frame around the oenter image description hereutside' in the animation on the input screen. The problem specifically affects the second character of the input text.

I implemented a custom typewriter effect using the useTypewriter hook in my React application. The hook takes two parameters, text (representing the input text to animate) and speed (determining the animation speed). The typewriter effect is achieved by incrementally updating the displayText state, adding each character from the text at specified intervals. However, I am currently facing an issue where the typewriter effect is missing an alphabet in the displayed text. For instance, when using the text 'tarot card with intricate detailed frame around the outside,' it displays 'trot card with intricate detailed frame around the outside' with the letter 'a' missing. I have tried adjusting the code and experimenting with different approaches, but the issue persists.

 export const useTypewriter = (text: string, speed: number) => {
  const [displayText, setDisplayText] = useState("");
  useEffect(() => {
    let i = 0;
    const typingInterval = setInterval(() => {
      if (i < text.length) {
        setDisplayText((prevText) => prevText + text.charAt(i));
        i++;
      } else {
        clearInterval(typingInterval);
      }
    }, speed);
    return () => {
      setDisplayText("");
      clearInterval(typingInterval);
    };
  }, [text, speed]);
  return displayText;
};
1

There are 1 best solutions below

0
John Ruddell On BEST ANSWER

Your issue is due to how React state updates are batched, the reason why the second character is skipped is because i is incremented 2 times before the set state inner function has run once. To get this kind of precision you can consider using a ref to store your string value, or you can use a substr method to pull out an index range from the string.

export const useTypewriter = (text: string, speed: number) => {
  const [displayText, setDisplayText] = useState("");
  const idx = useRef(0);
  const displayTextRef = useRef("");
  useEffect(() => {
    const typingInterval = setInterval(() => {
      if (idx.current < text.length) {
        displayTextRef.current += text.charAt(idx.current);
        setDisplayText(() => displayTextRef.current);
        idx.current += 1;
      } else {
        clearInterval(typingInterval);
      }
    }, speed);
    return () => {
      setDisplayText("");
      clearInterval(typingInterval);
    };
  }, [text, speed]);
  return displayText;
};

An example of it running