diff --git a/hooks/useTypewriter.ts b/hooks/useTypewriter.ts index 6e01db0..624020e 100644 --- a/hooks/useTypewriter.ts +++ b/hooks/useTypewriter.ts @@ -1,20 +1,31 @@ "use client"; -import { useEffect, useState } from "react"; +import { useEffect, useRef, useState } from "react"; +/** + * Cycles through `words` with a typewriter effect. + * @param words - Must be referentially stable (define outside component or wrap in useMemo). + * @param typingSpeed - ms per character while typing (default 80) + * @param deletingSpeed - ms per character while deleting (default 40) + * @param pauseMs - ms to pause after a full word is typed (default 2000) + * @returns The currently displayed string + */ export function useTypewriter(words: string[], typingSpeed = 80, deletingSpeed = 40, pauseMs = 2000) { const [displayed, setDisplayed] = useState(""); const [wordIndex, setWordIndex] = useState(0); const [isDeleting, setIsDeleting] = useState(false); + const pauseRef = useRef | null>(null); useEffect(() => { + if (!words.length) return; + const current = words[wordIndex % words.length]; const timeout = setTimeout(() => { if (!isDeleting) { setDisplayed(current.slice(0, displayed.length + 1)); if (displayed.length + 1 === current.length) { - setTimeout(() => setIsDeleting(true), pauseMs); + pauseRef.current = setTimeout(() => setIsDeleting(true), pauseMs); } } else { setDisplayed(current.slice(0, displayed.length - 1)); @@ -25,7 +36,10 @@ export function useTypewriter(words: string[], typingSpeed = 80, deletingSpeed = } }, isDeleting ? deletingSpeed : typingSpeed); - return () => clearTimeout(timeout); + return () => { + clearTimeout(timeout); + if (pauseRef.current) clearTimeout(pauseRef.current); + }; }, [displayed, isDeleting, wordIndex, words, typingSpeed, deletingSpeed, pauseMs]); return displayed;