在下面的示例中,我们有一个按钮,用于启动和上载,并在上载过程中禁用自身。上传完成后,它会重新启用自己。
有没有可能,由于React的setState的异步特性,对于一个点击速度非常快的用户来说,在按钮被禁用之前触发两次onClark回调?
请不要回答如何避免这种情况的解决方案,我想知道这种情况是否可能,如果可能的话,如何重现它。
import React from 'react';
import ReactDOM from 'react-dom';
class App extends React.Component {
constructor (props) {
super(props)
this.state = {
uploadDisabled: false
}
}
upload = () => {
this.setState({
uploadDisabled: true
})
fetch('/upload').then(() => {
this.setState({
uploadDisabled: false
})
})
}
render () {
return (
<div>
<button disabled={this.state.uploadDisabled} onClick={this.upload}>
Upload
</button>
</div>
)
}
}
我不确定这是否可能:
class Button extends React.Component {
constructor(){
super();
this.state = {disabled: false};
this.onClick = () => {
this.setState({
disabled: true
});
console.log('click')
setTimeout(() => {
this.setState({
disabled: false
});
}, 1000)
};
}
render() {
return <button onClick={this.onClick} disabled={this.state.disabled}>foo</button>;
}
}
ReactDOM.render(
<Button/>,
document.getElementById('container')
);
setTimeout(() => {
const button = document.getElementsByTagName('button')[0]
for(let i = 0; i < 2; i++) {
button.click()
}
}, 200)
https://jsbin.com/tamifaruqu/edit?html,js,控制台,输出
它只打印一次点击
我一直在做一些实验,想知道我的解决方案是否能达到目的。我在组件上定义了一个布尔属性,该属性不在状态上,并且在单击按钮时直接切换。代码如下:
class App extends React.Component {
// not on the state, will be toggled directly when the user presses the button
uploadInProgress = false;
// just for the purpose of experiment, has nothing to do with the solution
uploadCalledCount = 0;
state = {
uploadDisabled: false
}
upload = () => {
if (this.uploadInProgress) return;
this.uploadInProgress = true;
this.uploadCalledCount++;
// let's experiment and make setState be called after 1 second
// to emulate some delay for the purpose of experiment
setTimeout(() => {
this.setState({ uploadDisabled: true })
}, 1000);
// emulate a long api call, for the purpose of experiment
setTimeout(() => {
this.setState(
{ uploadDisabled: false },
() => { this.uploadInProgress = false; })
}, 3000);
}
render() {
return (
<div>
<button disabled={this.state.uploadDisabled} onClick={this.upload}>
Upload
</button>
<br />
{this.uploadCalledCount}
</div>)
}
}
下面是一个运行示例om codesandbox。
检查方法:焦虑和不耐烦的用户尽可能多地点击按钮,按钮将在setState
调用前设置的1秒延迟后被禁用,然后upload
功能的实际调用次数将出现在屏幕上(在state
更改后),然后,该按钮在3秒延迟后再次启用。
import debounce from "lodash/debounce"
//import debounce at the top
upload = () => {
this.setState({
uploadDisabled: true
})
fetch('/upload').then(() => {
this.setState({
uploadDisabled: false
})
})
}
onClickUpload = () => {
_debounce(upload, time) // time is the number of milliseconds to delay.
}
render () {
return (
<div>
<button disabled={this.state.uploadDisabled} onClick={this.onClickUpload}>
Upload
</button>
</div>
)
}
这可能有帮助