【问题标题】:How can I setState on just one array element at once?如何一次只在一个数组元素上设置状态?
【发布时间】:2017-07-24 14:57:14
【问题描述】:

我是 ReactJS 和 javascript 的初学者

我有一个关于How can I setState on two array elements at once?的问题

在那个问题上,响应者告诉我无论如何都不要改变状态。

但我还有一个问题,如果我想使用clearInterval() 停止倒数计时器,当计时器到达 0 时。

在我对 codePen Timer Demo 2 的新尝试中,

我使用find()方法找到时间为0的元素,并使用clearInterval停止倒计时,但是如果一个元素的时间到了0而另一个不是,则全部停止。

我该如何解决?

const timers = [
  {
    id: 1,
    time: 5,
    timeIsUp: false,
  },
  {
    id: 2,
    time: 10,
    timeIsUp: false,
  },
];
class Clock extends React.Component {
  constructor(props) {
    super(props);
    this.state= {
      timers,
    }
    this.time_controller = 0;

  }
  componentDidMount(){
    this.time_controller = setInterval(() =>{
      this.countDown();
    }, 1000)
  }
  countDown(){
    const foundTimers = this.state.timers.map(timer => ({
      ...timer,
      time: timer.time-1
    }));
    this.setState({timers: foundTimers});
    const foundTimer = foundTimers.find(timer => timer.time === 0 );
    if(!!foundTimer){
      clearInterval(this.time_controller);
    }
  }

  renderTimers(){
    return(
     this.state.timers.map((timer) =>{
      return(
        <div key = {timer.id} >
          <div>{timer.time}</div>
        </div>
      )
    })
    )
  }

  render() {

    return (
      <div>
        {this.renderTimers()}
      </div>
    );
  }
}

ReactDOM.render(
  <Clock />,
  document.getElementById('root')
);

【问题讨论】:

  • @AndrewLi 所以我需要将 time_controller 添加到数组元素中?

标签: javascript reactjs


【解决方案1】:

这和状态管理无关,很简单的逻辑:

  1. 在所有计时器倒计时之前,请勿关闭间隔。
  2. 不要减少已经达到零的计时器的时间。

旁注:timeIsUp 标志确实没有任何理由,是吗? time === 0 表示时间到了,不是吗?复制状态为您在一个地方更新它而不是其他地方打开了错误的大门。

类似这些,但细节不如概念重要:

countDown() {
  let updated = false;
  let keepTicking = false;
  const updatedTimers = this.state.timers.map(timer => {
    // Note that if we don't modify `timer`, we return it for reuse
    if (timer.time > 0) {
      timer = {...timer, time: timer.time - 1};
      updated = true;
      keepTicking = timer.time > 0;
    }
    return timer;
  });
  if (updated) {
    // We changed somthing, update
    this.setState({timers: updatedTimers});
  }
  if (this.time_controller !== 0 && !keepTicking) {
    // No more active timers, stop ticking
    clearInterval(this.time_controller);
    this.time_controller = 0;
  }
}

注意我在清空定时器的时候清空了定时器句柄,所以我们知道它是否在运行。

请注意,在该示例中,我采用了带有副作用的map 回调的务实方法(设置updatedkeepTicking 标志)。我对这样的局部副作用没有意见,但是有一个重大的运动会发生。

无副作用的版本至少会部分循环计时器不止一次:

countDown() {
  const timers = this.state.timers;
  // Need to update?
  if (timers.find(t => t.time > 0)) {
    const updatedTimers = timers.map(timer => (
      // If we don't change a timer, we can reuse it
      timer.time === 0 ? timer : {...timer, time: timer.time - 1}
    );
    this.setState({timers: updatedTimers});
  }
  // Done ticking?
  if (this.time_controller !== 0 && updatedTimers.every(t => t.time === 0)) {
    clearInterval(this.time_controller);
    this.time_controller = 0;
  }
}

请注意代码如何更简洁(因为我们仅在需要时才单独表达我们正在做的每件事)但效率较低(我们可能不得不努力找到接近尾声的计时器需要更新并保持我们滴答作响)。 90% 的情况下,效率无关紧要,timers 将少于几千个条目。所以有一个论据。

【讨论】:

  • 不关闭间隔停止倒计时怎么办?
  • @ChamperWu:如果time 已经为0,你就不要减少time。我添加了一些代码,但重要的是概念。
  • @T.J.Crowder 好吧,既然你已经添加了代码,我应该删除我的答案吗?
  • @AndrewLi:FWIW,我认为你不需要。
  • @ChamperWu:已修复。还在代码上添加了一些漫不经心的注释...... :-)
猜你喜欢
  • 2020-06-22
  • 2018-09-20
  • 2021-11-09
  • 2022-11-30
  • 1970-01-01
  • 2012-04-23
  • 1970-01-01
  • 1970-01-01
  • 2021-02-06
相关资源
最近更新 更多