Refactor useTypingEffect to fix timer leak and optimize re-renders #5
@@ -1,4 +1,4 @@
|
||||
import { useState, useEffect } from 'react';
|
||||
import { useState, useEffect, useMemo } from 'react';
|
||||
|
||||
interface UseTypingEffectOptions {
|
||||
words: string[];
|
||||
@@ -18,25 +18,33 @@ export function useTypingEffect({
|
||||
const [isDeleting, setIsDeleting] = useState(false);
|
||||
const [isPaused, setIsPaused] = useState(false);
|
||||
|
||||
// Stabilize words array to prevent unnecessary effect resets on re-renders
|
||||
// when the array reference changes but content remains the same.
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
const stableWords = useMemo(() => words, [JSON.stringify(words)]);
|
||||
|
||||
useEffect(() => {
|
||||
let timer: ReturnType<typeof setTimeout>;
|
||||
|
||||
if (isPaused) {
|
||||
timer = setTimeout(() => {
|
||||
setIsPaused(false);
|
||||
setIsDeleting(true);
|
||||
}, pauseDuration);
|
||||
} else {
|
||||
const currentWord = words[currentWordIndex];
|
||||
const currentWord = stableWords[currentWordIndex];
|
||||
const isWordDeleted = isDeleting && currentText === '';
|
||||
|
||||
if (isWordDeleted) {
|
||||
// If word is fully deleted, move to next word immediately (no timer)
|
||||
if (!isPaused && isWordDeleted) {
|
||||
setIsDeleting(false);
|
||||
setCurrentWordIndex((prev) => (prev + 1) % words.length);
|
||||
} else {
|
||||
const speed = isDeleting ? deletingSpeed : typingSpeed;
|
||||
setCurrentWordIndex((prev) => (prev + 1) % stableWords.length);
|
||||
return;
|
||||
}
|
||||
|
||||
// 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 {
|
||||
@@ -47,9 +55,8 @@ export function useTypingEffect({
|
||||
setIsPaused(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}, speed);
|
||||
}
|
||||
}
|
||||
|
||||
return () => clearTimeout(timer);
|
||||
}, [
|
||||
@@ -57,7 +64,7 @@ export function useTypingEffect({
|
||||
isDeleting,
|
||||
isPaused,
|
||||
currentWordIndex,
|
||||
words,
|
||||
stableWords,
|
||||
typingSpeed,
|
||||
deletingSpeed,
|
||||
pauseDuration,
|
||||
|
||||
Reference in New Issue
Block a user