React中useState更新字符串异常:打字效果丢失第二个字符
打字效果丢失第二个字符的问题排查与修复
我想实现打字效果,但第二个字符总是无法显示。比如要输出"Hello World",实际显示成"Hllo World"。我的代码如下:
export default function Error({ errorMsg }) { const [errorString, setErrorString] = useState(""); let i = 0; useEffect(() => { writeError(); }, []); function writeError() { let timer = Math.floor(Math.random() * (100 - 25 + 1)) + 25; setTimeout(() => { if (i < errorMsg.length) { setErrorString(errorString => ([ ...errorString, errorMsg.charAt(i) ])); i++; return writeError(); } }, timer); } return ( <div> <div id="wrp"> <div>{errorString}</div> </div> </div> ); }
问题原因
问题出在闭包对变量i的引用以及React状态更新的执行时机上:
setErrorString的更新回调是闭包,它捕获的是i变量的引用,而非调用setErrorString时的i值。- 原代码中调用
setErrorString后立即执行i++,若更新回调被延迟执行(比如React批处理更新场景),回调执行时i已经是自增后的值,导致本该添加的第二个字符(i=1对应的字符)被跳过,直接使用了i=2时的字符。
修复方案
将i++移至charAt方法调用中,确保获取当前字符的同时立即自增i,避免闭包引用带来的变量值不一致问题:
export default function Error({ errorMsg }) { const [errorString, setErrorString] = useState(""); let i = 0; useEffect(() => { writeError(); }, []); function writeError() { let timer = Math.floor(Math.random() * (100 - 25 + 1)) + 25; setTimeout(() => { if (i < errorMsg.length) { setErrorString(errorString => ([ ...errorString, errorMsg.charAt(i++) // 在这里自增i,确保取值与自增同步 ])); return writeError(); } }, timer); } return ( <div> <div id="wrp"> <div>{errorString}</div> </div> </div> ); }
这样修改后,charAt(i++)会先获取当前i对应的字符,再立即将i自增,无论更新回调何时执行,都能保证取到正确位置的字符,不会出现字符丢失的问题。
内容的提问来源于stack exchange,提问作者Jake
相关产品推荐
相关产品推荐

