递归的 setTimeout 没有按照预期的方式进行
Recursive `setTimeout` doesn't engage in expected way
我有一个范围输入字段,其数值在我拖动时会发生变化:
当我一直向右拖动时,输入的最大值增加了。然后,当我释放(onMouseUp 或 onTouchEnd)时,最大值减小,以便我可以进一步拖动并通过拖动继续增加最大值:
当我一直向左拖动时,输入的最小值减小了。然后,当我释放(onMouseUp 或 onTouchEnd)时,最小值增加,以便我可以进一步拖动并通过拖动继续减小最小值:
我的范围应该始终为 99。例如,如果我将最大值增加到 530,则最小值将为 431。
问题:
我为 min
和 max
值更改设置了两个递归 setTimeout
。
当用户第一次一直拖动到任一侧时,数字应该会慢慢变化。如果他们保持 2 秒,数字应该会增加得更快。相关代码:
// After arbitrary period, increase the rate at which the max value increments
this.fasterChangeStake = setTimeout(() => {
this.stakeChangeTimeout = this.FAST_STAKE_CHANGE_TIMEOUT;
}, 2000);
这是第一次成功。但随后的时间,它首先锁定到更快的超时:
尽管我在拖动结束时清除了超时:
clearTimeout(this.increaseStakeLimits);
clearTimeout(this.decreaseStakeLimits);
clearTimeout(this.timer);
为什么第一个(较慢的)超时不吸引人?
代码笔:https://codepen.io/alanbuchanan/pen/NgjKMa?editors=0010
JS:
const {observable, action} = mobx
const {observer} = mobxReact
const {Component} = React
@observer
class InputRange extends Component {
constructor() {
super();
this.INITIAL_STAKE_CHANGE_TIMEOUT = 200;
this.FAST_STAKE_CHANGE_TIMEOUT = 20;
this.SNAP_PERCENT = 10;
this.increaseStakeLimits = this.increaseStakeLimits.bind(this);
this.decreaseStakeLimits = this.decreaseStakeLimits.bind(this);
}
@observable min = 0;
@observable max = 99;
@observable stakeChangeTimeout = this.INITIAL_STAKE_CHANGE_TIMEOUT;
@observable isIncreasing = false;
@observable isDecreasing = false;
@observable stake = 0;
@action updateStake = (amount) => {
if (amount > -1) {
this.stake = amount
}
};
increaseStakeLimits() {
const { updateStake } = this;
this.max = this.max += 1;
this.min = this.max - 99
updateStake(this.max);
// After arbitrary period, increase the rate at which the max value increments
this.fasterChangeStake = setTimeout(() => {
this.stakeChangeTimeout = this.FAST_STAKE_CHANGE_TIMEOUT;
}, 2000);
// Recursive call, like setInterval
this.timer = setTimeout(this.increaseStakeLimits, this.stakeChangeTimeout);
this.isIncreasing = true;
}
decreaseStakeLimits() {
console.warn('this.stake:', this.stake)
const { stake } = this
const { updateStake } = this;
this.min = this.min -= 1;
this.max = this.min + 99
updateStake(this.min);
// After arbitrary period, increase the rate at which the max value increments
this.fasterChangeStake = setTimeout(() => {
this.stakeChangeTimeout = this.FAST_STAKE_CHANGE_TIMEOUT;
}, 2000);
// Recursive call, like setInterval
this.timer = setTimeout(this.decreaseStakeLimits, this.stakeChangeTimeout);
this.isDecreasing = true;
}
handleStakeChange = e => {
clearTimeout(this.increaseStakeLimits);
clearTimeout(this.decreaseStakeLimits);
clearTimeout(this.timer);
const { updateStake } = this;
const { stake } = this;
const val = Number(e.target.value)
// User has scrolled all the way to the right
if (val >= this.max) {
console.warn("scrolled to right")
this.increaseStakeLimits();
// User has scrolled all the way to the left
} else if (val <= this.min) {
console.warn("scrolled to left")
if (val > -1) {
this.decreaseStakeLimits();
}
} else {
updateStake(val);
}
};
handleRelease = () => {
console.warn("RANGE:", this.max - this.min)
console.warn("released");
clearTimeout(this.fasterChangeStake);
clearTimeout(this.timer);
// Reset the timeout value to the initial one
this.stakeChangeTimeout = this.INITIAL_STAKE_CHANGE_TIMEOUT;
this.SNAP_PERCENT = 10
const snapAmount = this.SNAP_PERCENT
if (this.isIncreasing) {
this.max += snapAmount
}
if(this.isDecreasing && this.min > 0) {
this.min -= snapAmount
}
this.isIncreasing = false;
this.isDecreasing = false;
};
render() {
const { stake } = this;
const style = {
backgroundSize:
(stake - this.min) * 100 / (this.max - this.min) + "% 100%"
};
return (
<div className="rangeContainer">
<div>{this.stake}</div>
<div className="inputContainer">
<input
id="betRangeId"
type="range"
min={this.min}
max={this.max}
step="1"
ref={input => {
this.textInput = input;
}}
value={this.stake}
onChange={this.handleStakeChange}
onTouchEnd={this.handleRelease}
onMouseUp={this.handleRelease}
style={style}
/>
</div>
</div>
);
}
}
ReactDOM.render(<InputRange />, document.getElementById('root'))
真聪明UI!
increaseStakeLimits()
在用户将滑块保持在最右侧的整个过程中连续触发,因此您不断设置新的 setTimeouts 以在两秒后将 this.stakeChangeTimeout
更改为更短的间隔(并且不断为 this.fasterChangeStake
设置新值)。即使在 handleRelease()
尝试将间隔重置为更长的值后,它们仍会继续触发,这会迫使它回到快速模式,直到所有这些都触发(handleRelease()
中的 clearTimeout
只捕获一个其中。)
您只需设置一次超时即可解决此问题:
if (!this.fasterChangeStake) {
this.fasterChangeStake = setTimeout(() => {
this.stakeChangeTimeout = this.FAST_STAKE_CHANGE_TIMEOUT;
this.fasterChangeStake = false; // <-- also do this in handleRelease() after clearing the timeout
}, 2000);
}
我有一个范围输入字段,其数值在我拖动时会发生变化:
当我一直向右拖动时,输入的最大值增加了。然后,当我释放(onMouseUp 或 onTouchEnd)时,最大值减小,以便我可以进一步拖动并通过拖动继续增加最大值:
当我一直向左拖动时,输入的最小值减小了。然后,当我释放(onMouseUp 或 onTouchEnd)时,最小值增加,以便我可以进一步拖动并通过拖动继续减小最小值:
我的范围应该始终为 99。例如,如果我将最大值增加到 530,则最小值将为 431。
问题:
我为 min
和 max
值更改设置了两个递归 setTimeout
。
当用户第一次一直拖动到任一侧时,数字应该会慢慢变化。如果他们保持 2 秒,数字应该会增加得更快。相关代码:
// After arbitrary period, increase the rate at which the max value increments
this.fasterChangeStake = setTimeout(() => {
this.stakeChangeTimeout = this.FAST_STAKE_CHANGE_TIMEOUT;
}, 2000);
这是第一次成功。但随后的时间,它首先锁定到更快的超时:
尽管我在拖动结束时清除了超时:
clearTimeout(this.increaseStakeLimits);
clearTimeout(this.decreaseStakeLimits);
clearTimeout(this.timer);
为什么第一个(较慢的)超时不吸引人?
代码笔:https://codepen.io/alanbuchanan/pen/NgjKMa?editors=0010
JS:
const {observable, action} = mobx
const {observer} = mobxReact
const {Component} = React
@observer
class InputRange extends Component {
constructor() {
super();
this.INITIAL_STAKE_CHANGE_TIMEOUT = 200;
this.FAST_STAKE_CHANGE_TIMEOUT = 20;
this.SNAP_PERCENT = 10;
this.increaseStakeLimits = this.increaseStakeLimits.bind(this);
this.decreaseStakeLimits = this.decreaseStakeLimits.bind(this);
}
@observable min = 0;
@observable max = 99;
@observable stakeChangeTimeout = this.INITIAL_STAKE_CHANGE_TIMEOUT;
@observable isIncreasing = false;
@observable isDecreasing = false;
@observable stake = 0;
@action updateStake = (amount) => {
if (amount > -1) {
this.stake = amount
}
};
increaseStakeLimits() {
const { updateStake } = this;
this.max = this.max += 1;
this.min = this.max - 99
updateStake(this.max);
// After arbitrary period, increase the rate at which the max value increments
this.fasterChangeStake = setTimeout(() => {
this.stakeChangeTimeout = this.FAST_STAKE_CHANGE_TIMEOUT;
}, 2000);
// Recursive call, like setInterval
this.timer = setTimeout(this.increaseStakeLimits, this.stakeChangeTimeout);
this.isIncreasing = true;
}
decreaseStakeLimits() {
console.warn('this.stake:', this.stake)
const { stake } = this
const { updateStake } = this;
this.min = this.min -= 1;
this.max = this.min + 99
updateStake(this.min);
// After arbitrary period, increase the rate at which the max value increments
this.fasterChangeStake = setTimeout(() => {
this.stakeChangeTimeout = this.FAST_STAKE_CHANGE_TIMEOUT;
}, 2000);
// Recursive call, like setInterval
this.timer = setTimeout(this.decreaseStakeLimits, this.stakeChangeTimeout);
this.isDecreasing = true;
}
handleStakeChange = e => {
clearTimeout(this.increaseStakeLimits);
clearTimeout(this.decreaseStakeLimits);
clearTimeout(this.timer);
const { updateStake } = this;
const { stake } = this;
const val = Number(e.target.value)
// User has scrolled all the way to the right
if (val >= this.max) {
console.warn("scrolled to right")
this.increaseStakeLimits();
// User has scrolled all the way to the left
} else if (val <= this.min) {
console.warn("scrolled to left")
if (val > -1) {
this.decreaseStakeLimits();
}
} else {
updateStake(val);
}
};
handleRelease = () => {
console.warn("RANGE:", this.max - this.min)
console.warn("released");
clearTimeout(this.fasterChangeStake);
clearTimeout(this.timer);
// Reset the timeout value to the initial one
this.stakeChangeTimeout = this.INITIAL_STAKE_CHANGE_TIMEOUT;
this.SNAP_PERCENT = 10
const snapAmount = this.SNAP_PERCENT
if (this.isIncreasing) {
this.max += snapAmount
}
if(this.isDecreasing && this.min > 0) {
this.min -= snapAmount
}
this.isIncreasing = false;
this.isDecreasing = false;
};
render() {
const { stake } = this;
const style = {
backgroundSize:
(stake - this.min) * 100 / (this.max - this.min) + "% 100%"
};
return (
<div className="rangeContainer">
<div>{this.stake}</div>
<div className="inputContainer">
<input
id="betRangeId"
type="range"
min={this.min}
max={this.max}
step="1"
ref={input => {
this.textInput = input;
}}
value={this.stake}
onChange={this.handleStakeChange}
onTouchEnd={this.handleRelease}
onMouseUp={this.handleRelease}
style={style}
/>
</div>
</div>
);
}
}
ReactDOM.render(<InputRange />, document.getElementById('root'))
真聪明UI!
increaseStakeLimits()
在用户将滑块保持在最右侧的整个过程中连续触发,因此您不断设置新的 setTimeouts 以在两秒后将 this.stakeChangeTimeout
更改为更短的间隔(并且不断为 this.fasterChangeStake
设置新值)。即使在 handleRelease()
尝试将间隔重置为更长的值后,它们仍会继续触发,这会迫使它回到快速模式,直到所有这些都触发(handleRelease()
中的 clearTimeout
只捕获一个其中。)
您只需设置一次超时即可解决此问题:
if (!this.fasterChangeStake) {
this.fasterChangeStake = setTimeout(() => {
this.stakeChangeTimeout = this.FAST_STAKE_CHANGE_TIMEOUT;
this.fasterChangeStake = false; // <-- also do this in handleRelease() after clearing the timeout
}, 2000);
}