如何在React中使用useState实现逐字显示文本?问题求修复
修复React逐字追加文本时的重复显示问题
问题原因
- 文本替换而非追加:如果不用函数式更新(
setText(prev => prev + letter)),直接写setText(text + letter)会捕获组件初始渲染时的text值(空字符串),导致每次定时器触发时都把文本设为"" + letter,最终文本被替换而非追加。 - 字母重复显示: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
相关产品推荐
相关产品推荐

