微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

setInterval + React钩子导致组件内的多次更新

如何解决setInterval + React钩子导致组件内的多次更新

我正在构建一个秒表UI,以秒为单位显示时间。单击按钮,计时器将开始向上计数,并在再次单击时停止。用户应该可以再次启动它。

我遇到的问题是我可以使setInterval正常工作,但是一旦包含setTime钩子,组件就会更新以在UI中渲染时间,但setInterval实例被多次调用。这会导致奇怪的渲染行为。

const Timer = () => {
    const [time,setTime] = useState(0)
    let timer

    const startStopTimer = () => {
        if (!timer) timer = setInterval(() => setTime(time++),1000)
        else {
           clearInterval(timer)
           timer = null
        }
    }

    return (
            <div>
               <p>Time: {time} seconds</p>
               <Button 
                   onClick={() => {
                      startStopTimer()
                   }
               > Start/Stop </Button>
            </div>
           )
}

示例行为为:

  1. 用户单击“开始/停止”
  2. 计时器从0开始并向上计数
  3. 用户单击“开始/停止”
  4. 计时器立即停止
  5. 用户单击“开始/停止”
  6. 计时器从上次停止的地方继续

解决方法

这是React挂钩中过时关闭的经典示例,在调用os.chdir('/content/drive/My Drive') for file in glob.glob("*.txt"): with open(file,'r',encoding = 'utf-8',errors='replace') as file: Text = file.read() print(my_function(Text)) 之后,您的time的setInterval值不变。通过以下方式更改代码:

setTime

setInterval(() => setTime(currentTime => currentTime + 1),1000)就像类组件的setTime一样也接受一个以当前值作为第一个参数的回调函数

此外,setState变量在您的代码中是无用的,因为在每次重新渲染时,该变量都是未定义的,并且您将无法访问timer的返回值,因此它将重新初始化setInterval。要使用setInterval,可以将useRef的返回值存储在setInterval中,随后的重新渲染后将可以使用该返回值,因此无需重新初始化setInterval,也可以使用.current

解决方案:

clearInterval
const {useState,useRef} = React;
const {render} = ReactDOM;

const Timer = () => {
  const [time,setTime] = useState(0);
  const timer = useRef(null);
  const startStopTimer = () => {
    if (!timer.current) {
      timer.current = setInterval(() => setTime(currentTime => currentTime + 1),1000);
    } else {
      clearInterval(timer.current);
      timer.current = null;
    }
  };

  return (
    <div>
      <p>Time: {time} seconds</p>
      <button
        onClick={startStopTimer}
      >
        Start/Stop
      </button>
    </div>
  );
};

render(<Timer />,document.getElementById("root"));

,

这里是使用react类组件的示例。本示例跟踪开始时间,而不是按一定间隔添加一些值。然后,当您停止计时器时,它将累积经过的时间。

传递给setInterval的回调可能不会总是每n ms被准确地调用。如果JavaScript引擎繁忙,则可能需要花费几毫秒的时间。保持计数器运行时间越长,它就会慢慢抵消实际经过的时间。

const {Component} = React;
const {render} = ReactDOM;

class StopWatch extends Component {
  state = {startTime: null,accTime: 0,intervalId: null};
  
  componentWillUnmount() {
    clearInterval(this.state.intervalId);
  }
  
  ms() {
    const {startTime,accTime} = this.state;
    if (!startTime) return accTime;
    return Date.now() - startTime + accTime;
  }
  
  start = () => {
    this.setState({
      startTime: Date.now(),intervalId: setInterval(() => this.forceUpdate(),10)
    });
  }
  
  stop = () => {
    clearInterval(this.state.intervalId);
    this.setState({
      startTime: null,accTime: this.ms(),intervalId: null
    });
  }
  
  reset = () => {
    this.setState({
      accTime: 0,startTime: this.state.startTime && Date.now()
    });
  }

  render() {
    return (
      <div>
        <h1>{this.ms() / 1000}</h1>
        {this.state.startTime
          ? <button onClick={this.stop}>stop</button>
          : <button onClick={this.start}>start</button>}
        <button onClick={this.reset}>reset</button>
      </div>
    );
  }
}

render(<StopWatch />,document.getElementById("stop-watch"));
<script src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
<div id="stop-watch"></div>

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。