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
相关产品推荐
相关产品推荐

