You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.01 13:16:08