在下面的示例中,我们有一个启动和上传的按钮,并在上传过程中自行禁用。它在上传完成后重新启用。
由于React的setState的异步特性,对于点击速度非常快的用户,是否有可能在禁用该按钮之前触发两次onClick回调?
请不要回答有关如何避免这种情况的解决方案,我想知道这种情况是否可能在第一时间,如果可能,如何重现它。
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>
)
}
}
答案 0 :(得分:1)
我不确定是否可能:
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,console,output
只打印一次click
答案 1 :(得分:1)
我一直在尝试,并想知道我的解决方案是否可以解决问题。我在不在状态上的组件上定义了一个布尔属性 并在按钮点击时直接切换。这是代码:
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
更改后),然后再次延迟3秒后按钮启用。
答案 2 :(得分:0)
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>
)
}
这可能会有所帮助
答案 3 :(得分:0)
最简单的方法是lodash使用_.debounce(func, [wait=0], [options={}])
此功能忽略了等待&#39;等事件的事件触发器。一段时间
答案 4 :(得分:0)
这只是一个想法,但也许你可以使用onMouseDown和onMouseUp而不仅仅是onClick。 React的事件系统也提供了一个onDoubleClick事件,所以也许你可以尝试拦截它。
您可以在此处找到有关React事件的更多信息:https://reactjs.org/docs/events.html#mouse-events
答案 5 :(得分:0)
您可以为按钮创建参考,并使用其参考启用或禁用它。
<button ref="btn" onClick={this.onClick}>Send</button>
在onclick中,你可以禁用它
this.refs.btn.setAttribute("disabled", "disabled");
要重新启用,请执行
this.refs.btn.removeAttribute("disabled");
参考link