我是 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')
);
最佳答案
这与状态管理无关,这是简单的逻辑:
- 在对所有计时器进行倒计时之前,请勿关闭间隔。
- 不要减少已经达到零的计时器的时间。
旁注: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
回调的实用方法(设置 updated
和 keepTicking
标志)。我对这样的局部副作用没有问题,但有一个重大的进展会出现这种情况。
无副作用的版本至少会部分地多次循环计时器:
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% 的情况下,效率并不重要,计时器的长度将少于几千个条目。所以这是有争议的。
关于javascript - 如何一次只在一个数组元素上 setState?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45283826/