React打字应用:Accuracy与WPM传递后重置为0的问题
React打字应用:结果页Accuracy和WPM值显示后重置为0的问题
我开发了一个简单的React打字应用,遇到一个问题:当把Accuracy(准确率)和WPM(每分钟打字数)传递给ResultsPage.js组件时,这两个值在App.js里显示正常,通过console.log确认传递给ResultsPage时也正确,但随后会被重置为0。
相关代码
App.js的return代码块
return ( <div> <h1>Type the paragraph:</h1> <h2>{currentParagraph}</h2> <textarea ref={inputRef} value={typedText} onChange={handleChange} disabled={!isPlaying} rows={6} style={{ width: "100%" }} /> {showResults ? ( <ResultsPage accuracy={accuracy} wpm={wpm} onTryAgain={() => setShowResults(false)} /> ) : ( <div> {isPlaying ? ( <div> Time remaining: {timeRemaining} <br /> Accuracy: {accuracy.toFixed(2)}%<p>WPM: {wpm.toFixed(0)}</p> </div> ) : ( <div> <label htmlFor="timer-length">Timer length:</label> <input id="timer-length" type="number" value={timerLength} onChange={(event) => setTimerLength(Number(event.target.value))} /> <button onClick={handleStartClick}>Start</button> </div> )} </div> )} </div> );
计算WPM和Accuracy的useEffect代码
useEffect(() => { // Calculate the accuracy as the number of correctly typed words divided by the total number of words const currentWords = currentParagraph.split(" "); const totalWords = currentWords.length; const typedWords = typedText.split(" "); const correctWords = typedWords.reduce((total, word, index) => { if (word === currentWords[index]) { return total + 1; } return total; }, 0); setAccuracy((correctWords / typedWords.length) * 100); // console.log("acc" + accuracy); // Calculate the WPM as the number of correctly typed words divided by the time elapsed, multiplied by 60 seconds const elapsedTime = timerLength - timeRemaining; let totalChars = 0; typedWords.forEach((word, index) => { if (word === currentWords[index]) { totalChars += word.length + 1; // +1 to count the space } }); setWpm((totalChars / 5 / elapsedTime) * 60); //console.log("wpm" + wpm); }, [currentParagraph, typedText, timerLength, timeRemaining]);
ResultsPage.js代码
function ResultsPage(props) { // Set default values for accuracy and wpm const { accuracy = 0, wpm = 0, onTryAgain } = props; console.log("," + accuracy + "," + wpm); return ( <div> <p>Your accuracy was {accuracy}%</p> <p>Your WPM was {wpm}</p> <button onClick={onTryAgain}>Try Again</button> </div> ); } export default ResultsPage;
运行截图
应用运行时的输出

计时器结束后ResultsPage组件显示结果

问题原因
问题出在展示结果页后,实时计算Accuracy和WPM的useEffect仍在触发:
- 当计时器结束,
showResults变为true,但timeRemaining可能因未清除的计时器继续变化,或者其他依赖项(如typedText)在某些情况下被重置,导致useEffect重新执行。 - 重新计算时,可能出现
typedWords.length为0或elapsedTime异常的情况,最终将accuracy和wpm设置为0或NaN,覆盖了之前的正确值。
解决方法
方法一:阻止结果页状态下的实时计算
修改useEffect,添加判断逻辑,当展示结果页时跳过计算,并处理除以0的异常情况:
useEffect(() => { // 展示结果页时直接返回,不执行计算 if (showResults) return; const currentWords = currentParagraph.split(" "); const typedWords = typedText.split(" "); // 处理输入为空的情况,避免除以0 if (typedWords.length === 0) { setAccuracy(0); setWpm(0); return; } // 计算准确率 const correctWords = typedWords.reduce((total, word, index) => { return word === currentWords[index] ? total + 1 : total; }, 0); setAccuracy((correctWords / typedWords.length) * 100); // 计算WPM,处理时间为0的异常 const elapsedTime = timerLength - timeRemaining; if (elapsedTime === 0) { setWpm(0); return; } let totalChars = 0; typedWords.forEach((word, index) => { if (word === currentWords[index]) { totalChars += word.length + 1; // +1 统计空格 } }); setWpm((totalChars / 5 / elapsedTime) * 60); }, [currentParagraph, typedText, timerLength, timeRemaining, showResults]); // 添加showResults到依赖项
方法二:保存最终结果到独立状态
在计时器结束时,一次性计算并保存最终结果,结果页使用该独立状态而非实时计算值:
- 在App.js中添加最终结果状态:
const [finalAccuracy, setFinalAccuracy] = useState(0); const [finalWpm, setFinalWpm] = useState(0);
- 修改计时器结束逻辑(假设你有处理计时器结束的函数):
const handleTimerEnd = () => { setIsPlaying(false); const currentWords = currentParagraph.split(" "); const typedWords = typedText.split(" "); if (typedWords.length === 0) { setFinalAccuracy(0); setFinalWpm(0); } else { // 计算最终准确率 const correctWords = typedWords.reduce((total, word, index) => { return word === currentWords[index] ? total + 1 : total; }, 0); setFinalAccuracy((correctWords / typedWords.length) * 100); // 计算最终WPM const elapsedTime = timerLength; let totalChars = 0; typedWords.forEach((word, index) => { if (word === currentWords[index]) { totalChars += word.length + 1; } }); setFinalWpm((totalChars / 5 / elapsedTime) * 60); } setShowResults(true); };
- 修改ResultsPage的props传递:
<ResultsPage accuracy={finalAccuracy} wpm={finalWpm} onTryAgain={() => setShowResults(false)} />
这种方法更可靠,因为最终结果仅在计时器结束时计算一次,不会被后续的状态更新覆盖。
内容的提问来源于stack exchange,提问作者MakaveliN1
相关产品推荐
相关产品推荐

