diff --git a/src/hooks/useTypingEffect.ts b/src/hooks/useTypingEffect.ts index 2866685..4f1878f 100644 --- a/src/hooks/useTypingEffect.ts +++ b/src/hooks/useTypingEffect.ts @@ -1,4 +1,4 @@ -import { useState, useEffect, useMemo } from 'react'; +import { useState, useEffect, useMemo, useRef } from 'react'; interface UseTypingEffectOptions { words: string[]; @@ -23,44 +23,60 @@ export function useTypingEffect({ // eslint-disable-next-line react-hooks/exhaustive-deps const stableWords = useMemo(() => words, [JSON.stringify(words)]); + // Ref to hold the latest state for the timer loop + const stateRef = useRef({ currentText, isDeleting, isPaused }); + useEffect(() => { + stateRef.current = { currentText, isDeleting, isPaused }; + }); + + // Effect 1: Handle word switching when deleted + useEffect(() => { + if (isDeleting && currentText === '' && !isPaused) { + setIsDeleting(false); + setCurrentWordIndex((prev) => (prev + 1) % stableWords.length); + } + }, [currentText, isDeleting, isPaused, stableWords]); + + // Effect 2: Timer Loop useEffect(() => { let timer: ReturnType; const currentWord = stableWords[currentWordIndex]; - const isWordDeleted = isDeleting && currentText === ''; - // If word is fully deleted, move to next word immediately (no timer) - if (!isPaused && isWordDeleted) { - setIsDeleting(false); - setCurrentWordIndex((prev) => (prev + 1) % stableWords.length); - return; - } + const tick = () => { + const { currentText, isDeleting, isPaused } = stateRef.current; - // Determine speed based on state - const speed = isPaused ? pauseDuration : (isDeleting ? deletingSpeed : typingSpeed); - - timer = setTimeout(() => { if (isPaused) { setIsPaused(false); setIsDeleting(true); - } else { - // Typing logic - if (isDeleting) { - setCurrentText((prev) => prev.substring(0, prev.length - 1)); - } else { - const nextText = currentWord.substring(0, currentText.length + 1); - setCurrentText(nextText); + return; + } - if (nextText === currentWord) { - setIsPaused(true); - } + if (isDeleting) { + if (currentText === '') { + // Handled by the other effect + return; + } + setCurrentText((prev) => prev.substring(0, prev.length - 1)); + timer = setTimeout(tick, deletingSpeed); + } else { + const nextText = currentWord.substring(0, currentText.length + 1); + setCurrentText(nextText); + + if (nextText === currentWord) { + setIsPaused(true); + } else { + timer = setTimeout(tick, typingSpeed); } } - }, speed); + }; + + // Determine initial speed + const speed = isPaused ? pauseDuration : (isDeleting ? deletingSpeed : typingSpeed); + timer = setTimeout(tick, speed); return () => clearTimeout(timer); }, [ - currentText, isDeleting, isPaused, currentWordIndex,