React实现JS打字机效果时字符重复的问题排查与解决
解决打字机效果字符重复的问题
你遇到的字符重复问题,核心原因有两个:
- 旧定时器未清理:当
text或speed变化时,上一轮useEffect创建的定时器仍在运行,新定时器又被启动,多个打字逻辑同时执行,导致字符重复添加。 - 索引递增时机错误:原代码中
i++在setTimeout之后执行,结合setDisplayText的异步特性,可能导致同一索引的字符被多次处理。
修复后的代码
import { useState, useEffect, useRef } from 'react'; const useTypewriter = (text, speed = 20) => { const [displayText, setDisplayText] = useState(''); const timerRef = useRef(null); const indexRef = useRef(0); useEffect(() => { // 每次依赖变化时重置状态和索引 setDisplayText(''); indexRef.current = 0; const typeCharacter = () => { if (indexRef.current < text.length) { // 添加当前索引的字符 setDisplayText(prev => prev + text.charAt(indexRef.current)); // 立即递增索引,确保下一次处理新字符 indexRef.current++; // 启动下一次定时器 timerRef.current = setTimeout(typeCharacter, speed); } }; timerRef.current = setTimeout(typeCharacter, speed); // 清理逻辑:终止旧定时器,避免内存泄漏和重复执行 return () => { if (timerRef.current) { clearTimeout(timerRef.current); } }; }, [text, speed]); return displayText; };
关键修复说明
- 用
useRef跟踪状态:ref的值不会随组件重渲染重置,能同步获取最新的定时器ID和字符索引,避开状态更新的异步性问题。 - 强制清理定时器:
useEffect的返回函数会在组件卸载或依赖变化时执行,彻底终止旧的打字逻辑,防止多个实例同时运行。 - 调整索引递增时机:在添加当前字符后立即递增索引,保证每一轮打字逻辑只处理一个新字符,不会重复操作同一索引。
如果之前尝试用状态跟踪索引失败,问题出在状态更新的异步性——setIndex的新值要等到下一次渲染才生效,而定时器触发时仍在使用旧索引,导致重复添加字符。用ref则可以直接同步修改和读取索引值,彻底解决这个问题。
内容的提问来源于stack exchange,提问作者nickdip
相关产品推荐
相关产品推荐

