如何在React中动态修改组件样式,实现类似monkeytype的打字效果
解决方案
React属于声明式UI框架,不需要保存组件实例、手动调用组件方法来更新样式,所有UI变化都可以通过状态驱动实现,具体实现逻辑如下:
核心思路
在父组件TypeTest中维护3个核心状态,通过状态判断当前输入的单词位置,将状态通过props传递给Word组件,由Word组件根据props自主计算要渲染的样式即可:
- 当前输入的单词索引:用来定位「当前」正在输入的单词
- 当前单词的用户输入内容:用来做字符级别的正误判断
- 已完成单词的正误状态:用来给已输入完成的单词添加对应样式
代码实现
父组件TypeTest代码
import { useState, useEffect, useCallback } from 'react'; import Word from './word'; import './typeTest.css' export default function TypeTest() { const test1 = "the quick brown fox jumped over a tree and tripped over that tree the quick brown fox jumped over a tree and tripped over that tree the quick brown fox jumped over a tree and tripped over that tree the quick brown fox jumped over a tree and tripped over that tree"; const test1Arr = test1.split(' '); // 核心状态定义 const [currentWordIdx, setCurrentWordIdx] = useState(0); // 当前输入的单词索引 const [currentInput, setCurrentInput] = useState(''); // 当前单词的用户输入内容 const [completedWords, setCompletedWords] = useState([]); // 已完成单词的正误状态 // 键盘输入监听逻辑 const handleKeyDown = useCallback((e) => { // 按空格完成当前单词输入 if (e.key === ' ') { e.preventDefault(); const isCorrect = currentInput === test1Arr[currentWordIdx]; setCompletedWords(prev => [...prev, {isCorrect}]); setCurrentWordIdx(prev => prev + 1); setCurrentInput(''); return; } // 按退格且当前单词输入为空时,回到上一个单词修改 if (e.key === 'Backspace' && currentInput.length === 0 && currentWordIdx > 0) { setCurrentWordIdx(prev => prev - 1); setCompletedWords(prev => prev.slice(0, -1)); setCurrentInput(test1Arr[currentWordIdx - 1]); return; } // 普通字符输入逻辑,过滤功能键 if (e.key.length === 1 && !e.ctrlKey && !e.metaKey) { setCurrentInput(prev => prev + e.key); } }, [currentInput, currentWordIdx, test1Arr]); // 绑定/解绑全局键盘监听 useEffect(() => { window.addEventListener('keydown', handleKeyDown); return () => window.removeEventListener('keydown', handleKeyDown); }, [handleKeyDown]); return ( <div className='outer-word-container'> <div className="word-container" tabIndex={0}> {test1Arr.map((monoWord, index) => ( <Word key={index} inputWord={monoWord} isActive={index === currentWordIdx} // 标记是否是当前输入的单词 isCompleted={index < currentWordIdx} // 标记是否是已完成的单词 isCorrect={completedWords[index]?.isCorrect} // 已完成单词的正误标记 userInput={index === currentWordIdx ? currentInput : ''} // 当前单词的用户输入内容 /> ))} <Word inputWord="Scroll"/> </div> </div> ) }
Word组件示例代码
// Word.jsx export default function Word({inputWord, isActive, isCompleted, isCorrect, userInput}) { // 字符级别的样式渲染 const renderChars = () => { return inputWord.split('').map((char, idx) => { let className = 'char'; // 已输入字符判断正误 if (idx < userInput.length) { className += userInput[idx] === char ? ' correct' : ' wrong'; } // 当前待输入字符加光标高亮 if (idx === userInput.length && isActive) { className += ' current-cursor'; } return <span key={idx} className={className}>{char}</span> }) } // 单词级别的样式渲染 let wordClass = 'word'; if (isActive) wordClass += ' active-word'; if (isCompleted) wordClass += isCorrect ? ' correct-word' : ' wrong-word'; return <span className={wordClass}>{renderChars()}</span> }
补充说明
你可以根据需求自行调整CSS规则,比如给正确单词设置绿色、错误单词设置红色、当前输入单词加下划线高亮、待输入字符加闪烁光标效果等,所有样式变化都通过状态驱动,完全不需要手动操作组件实例或者调用组件方法,符合React的开发规范,也更容易维护。
内容的提问来源于stack exchange,提问作者sharkeater123
相关产品推荐
相关产品推荐

