为什么我的setstate函数会导致react.js中其他函数出现故障?

问题描述 投票:0回答:1

const [lap, setLap] = useState([]);

function start(){
       if(!isStart){
            starting = Date.now() - elapsed;
            isStart = true;
            timer = setInterval(update, 1000);  
            console.log("timer is activated");
    }
}
 function stop(){
        if(isStart){
            isStart = false;
            clearInterval(timer);
    }
}
function reset(){
        isStart = false;
        elapsed = 0;
        document.getElementById("time").textContent = "00:00:00";
        clearInterval(timer);
        starting = 0;
    }
}
function update(){
        var curr = Date.now();
        elapsed = curr - starting;
        var min = Math.floor(elapsed/(1000*60));
        var sec = Math.floor((elapsed/1000)%60);
        var msec = Math.floor((elapsed%1000)/10);
        var time = [min, sec, msec]
        time = time.map((element) => pad(element));
        var time_str = time[0] + ":" + time[1] + ":" + time[2];
        document.getElementById("time").textContent = time_str;
        console.log(time_str)
    }
const lap_update = (val) => {
        setLap(lap => [...lap, val]);
    }
<div id="lap" onClick={() => lap_update(document.getElementById("time").textContent)}>LAP</div>

以上是秒表计圈功能的代码片段。

不幸的是,单击我的 lap_update 函数时,导致启动/停止/重置功能发生故障。

(停止功能和重置功能不起作用,启动功能是双重渲染)。

我真的无法理解为什么,以及 setLap 语句中需要进行哪些更改。

请帮忙!

尝试找出 setLap 语句导致其他函数出现故障的问题(直到调用 Lap 函数,程序运行良好)。

javascript html reactjs setstate stopwatch
1个回答
0
投票

当您使用

lap
更新
setLap
状态时,它会导致组件重新渲染。因此,如果启动函数以某种方式与重新渲染耦合,它可能会被调用多次

为此,我们可以使用

useRef
钩子来跟踪计时器并检查秒表是否正在运行以避免重新渲染问题。这将确保状态更新不会干扰计时功能。

我对您的代码进行了这些更改,这是一个工作示例

export const Stopwatch = () => {
  const [lap, setLap] = useState([]);
  const [elapsed, setElapsed] = useState(0);
  const [isStart, setIsStart] = useState(false);
  const timerRef = useRef(null);
  const startingRef = useRef(0);

  const pad = (num) => (num < 10 ? '0' + num : num);

  const start = () => {
    if (!isStart) {
      startingRef.current = Date.now() - elapsed;
      setIsStart(true);
      timerRef.current = setInterval(update, 1000);
      console.log("timer is activated");
    }
  };

  const stop = () => {
    if (isStart) {
      setIsStart(false);
      clearInterval(timerRef.current);
    }
  };

  const reset = () => {
    setIsStart(false);
    setElapsed(0);
    document.getElementById("time").textContent = "00:00:00";
    clearInterval(timerRef.current);
    startingRef.current = 0;
  };

  const update = () => {
    const curr = Date.now();
    const elapsed = curr - startingRef.current;
    setElapsed(elapsed);
    const min = Math.floor(elapsed / (1000 * 60));
    const sec = Math.floor((elapsed / 1000) % 60);
    const msec = Math.floor((elapsed % 1000) / 10);
    const time = [min, sec, msec].map(pad);
    const time_str = `${time[0]}:${time[1]}:${time[2]}`;
    document.getElementById("time").textContent = time_str;
    console.log(time_str);
  };

  const lap_update = () => {
    setLap((lap) => [...lap, document.getElementById("time").textContent]);
  };

  return (
    <div>
      <div id="time">00:00:00</div>
      <div id="lap" onClick={lap_update}>LAP</div>
      <button onClick={start}>Start</button>
      <button onClick={stop}>Stop</button>
      <button onClick={reset}>Reset</button>
      <ul>
        {lap.map((lapTime, index) => (
          <li key={index}>{lapTime}</li>
        ))}
      </ul>
    </div>
  );
};

timerRef
跟踪间隔计时器。这将防止重新渲染引起的问题
startingRef
用于保持开始时间,即使组件重新渲染时也能进行一致的计时计算

了解更多关于

useRef
钩子这里

© www.soinside.com 2019 - 2024. All rights reserved.