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

React打字效果组件字符丢失与undefined问题原因咨询

React + Framer Motion 打字组件的闭包问题解析

原组件代码

TypingText.jsx

import { motion } from 'framer-motion';
import React, { useEffect, useState } from 'react';

const TypingText = ({ text, speed = 100 }) => {
  const [displayedText, setDisplayedText] = useState('');

  useEffect(() => {
    if (!text) return;
    let i = 0;
    const interval = setInterval(() => {
      setDisplayedText((prev) => prev + text[i]);
      i++;

      if (i >= text.length) clearInterval(interval);
    }, speed);

    return () => clearInterval(interval);
  }, [text, speed, setDisplayedText]);

  return (
    <motion.div className="font-mono text-2xl">{displayedText}</motion.div>
  );
};

export default TypingText;

Home.jsx

<TypingText text="Hi, I am Thulhid" />

问题现象

运行后输出异常:H, I am Thulhidundefined,表现为第二个字符"i"丢失,末尾多了undefined。

修复后的代码片段

将定时器内的代码调整为:

const char = text[i];
setDisplayedText((prev) => prev + char);

修改后组件恢复正常工作。

疑问解答

1. 为何直接在setDisplayedText回调中访问text[i]会出问题?

这是JavaScript闭包的变量引用特性 + 异步状态更新共同导致的:

  • useEffect里声明的i是一个共享变量,所有定时器回调和状态更新回调都通过闭包捕获它的引用,而非当前时刻的固定值。
  • 定时器回调的执行顺序是:先调用setDisplayedText传入更新回调,立刻执行i++修改变量值。但React的状态更新回调是异步执行的——等它真正运行时,i已经被后续的i++修改了。
  • 最终导致的结果是:部分更新回调会取到“超前”的i值,跳过本该显示的字符;当i超过文本长度时,text[i]返回undefined,就会在末尾出现异常字符。

2. 将text[i]提取为单独变量char为何能解决问题?

把text[i]赋值给char,相当于保存了当前定时器触发时刻的字符快照:

  • char是定时器回调内部的局部变量,每次定时器触发都会创建一个新的char,它的值是当前i对应字符的固定值,不会随后续i的变化而改变。
  • 状态更新回调引用的是这个局部变量char,而非共享的i变量,所以不管后续i怎么变化,更新回调都会使用当时保存的正确字符,不会再出现跳字符或undefined的问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.13 09:23:10