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

如何在React中使用useState实现逐字显示文本?问题求修复

修复React逐字追加文本时的重复显示问题

问题原因

  1. 文本替换而非追加:如果不用函数式更新(setText(prev => prev + letter)),直接写setText(text + letter)会捕获组件初始渲染时的text值(空字符串),导致每次定时器触发时都把文本设为"" + letter,最终文本被替换而非追加。
  2. 字母重复显示:React 18的<StrictMode>在开发环境中会重复执行Effect函数,导致你的forEach循环被执行两次,每个定时器都被注册了两次,最终每个字母被追加两次。

修复方案

方案1:添加定时器清理函数

通过Effect的清理函数清除所有定时器,避免重复注册:

const App = () => {
  const [text, setText] = React.useState(``);
  React.useEffect(() => {
    const timers = [];
    [`x`,`y`].forEach((letter, index) => {
      const timerId = setTimeout(()=> {
        // 函数式更新确保基于最新状态追加字符
        setText(prevText => prevText + letter)
      }, 500 * index)
      timers.push(timerId);
    })
    // 组件卸载或Effect重新执行时清除所有定时器
    return () => {
      timers.forEach(id => clearTimeout(id));
    }
  }, [])
  return <>{text}</>
}

ReactDOM.createRoot(document.getElementById(`root`))
  .render(
    <React.StrictMode>
      <App />
    </React.StrictMode>
  );

方案2:用Ref标记初始化状态

通过Ref标记Effect是否已经执行过,避免重复执行逻辑:

const App = () => {
  const [text, setText] = React.useState(``);
  const hasInitialized = React.useRef(false);
  
  React.useEffect(() => {
    if (hasInitialized.current) return;
    hasInitialized.current = true;
    
    [`x`,`y`].forEach((letter, index) => {
      setTimeout(()=> {
        setText(prevText => prevText + letter)
      }, 500 * index)
    })
  }, [])
  return <>{text}</>
}

ReactDOM.createRoot(document.getElementById(`root`))
  .render(
    <React.StrictMode>
      <App />
    </React.StrictMode>
  );

关键说明

  • 必须使用setText(prevText => prevText + letter)这种函数式更新,确保每次都基于最新的状态追加字符,而非捕获初始的空状态。
  • StrictMode的双重执行是React开发环境的调试机制,用于检测潜在的副作用问题,生产环境不会触发该行为,但开发环境中需要处理以避免异常。

内容的提问来源于stack exchange,提问作者JDD

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.05 15:25:15