如何一次只在一个数组元素上设置state?

时间:2017-07-24 14:57:14

标签: javascript reactjs

我是ReactJS和javascript的初学者

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

的问题

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

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

在我对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')
);

1 个答案:

答案 0 :(得分:3)

这与国家管理无关,这是一个简单的逻辑:

  1. 不要关闭间隔,直到你倒计时所有的计时器。
  2. 不要减少已经达到零的计时器的时间。
  3. 旁注: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将少于几千个条目。所以有一个论据。