You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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组件显示结果

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到依赖项

方法二:保存最终结果到独立状态

在计时器结束时,一次性计算并保存最终结果,结果页使用该独立状态而非实时计算值:

  1. 在App.js中添加最终结果状态:
const [finalAccuracy, setFinalAccuracy] = useState(0);
const [finalWpm, setFinalWpm] = useState(0);
  1. 修改计时器结束逻辑(假设你有处理计时器结束的函数):
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);
};
  1. 修改ResultsPage的props传递:
<ResultsPage
  accuracy={finalAccuracy}
  wpm={finalWpm}
  onTryAgain={() => setShowResults(false)}
/>

这种方法更可靠,因为最终结果仅在计时器结束时计算一次,不会被后续的状态更新覆盖。

内容的提问来源于stack exchange,提问作者MakaveliN1

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.05 16:45:31