ReactJs:防止多次按下按钮
Posted
技术标签:
【中文标题】ReactJs:防止多次按下按钮【英文标题】:ReactJs: Prevent multiple times button press 【发布时间】:2016-05-20 20:15:55 【问题描述】:在我的 React 组件中,我有一个按钮,用于在单击时通过 AJAX 发送一些数据。我只需要第一次发生,即在第一次使用后禁用按钮。
我是如何尝试这样做的:
var UploadArea = React.createClass(
getInitialState()
return
showUploadButton: true
;
,
disableUploadButton(callback)
this.setState( showUploadButton: false , callback);
,
// This was simpler before I started trying everything I could think of
onClickUploadFile()
if (!this.state.showUploadButton)
return;
this.disableUploadButton(function()
$.ajax(
[...]
);
);
,
render()
var uploadButton;
if (this.state.showUploadButton)
uploadButton = (
<button onClick=this.onClickUploadFile>Send</button>
);
return (
<div>
uploadButton
</div>
);
);
我认为发生的情况是状态变量 showUploadButton
没有立即更新,React 文档说这是意料之中的。
我怎样才能强制按钮在被点击的那一刻被禁用或完全消失?
【问题讨论】:
您是否注意到问题或只是想知道?你能双份提交吗? Formik库默认处理这个问题,在Formik官网搜索issubmitting 请查看@cquezel 的答案以获得更简洁的方法。使用 ref 禁用表单控件(显示在接受的答案中)是一件老事,可能与 ReactJs 的初始版本有关。 【参考方案1】:您可以做的是在单击后禁用按钮并将其留在页面中(不可点击的元素)。
要实现这一点,您必须向按钮元素添加 ref
<button ref="btn" onClick=this.onClickUploadFile>Send</button>
然后在 onClickUploadFile 函数上禁用按钮
this.refs.btn.setAttribute("disabled", "disabled");
然后,您可以相应地设置禁用按钮的样式,以向用户提供一些反馈
.btn:disabled /* styles go here */
如果需要,请确保重新启用它
this.refs.btn.removeAttribute("disabled");
更新:在 React 中处理 refs 的首选方式是使用函数而不是字符串。
<button
ref=btn => this.btn = btn;
onClick=this.onClickUploadFile
>Send</button>
this.btn.setAttribute("disabled", "disabled");
this.btn.removeAttribute("disabled");
更新:使用反应钩子
import useRef from 'react';
let btnRef = useRef();
const onBtnClick = e =>
if(btnRef.current)
btnRef.current.setAttribute("disabled", "disabled");
<button ref=btnRef onClick=onBtnClick>Send</button>
这是一个使用您提供的代码的小例子 https://jsfiddle.net/69z2wepo/30824/
【讨论】:
这让我成功了一半,但 React 团队不赞成给 ref 一个字符串值,而是使用回调:reactjs.org/docs/refs-and-the-dom.html 它给了我一个错误'TypeError: self.btn.setAttribute is not a function' :( 最好的分析器是debounce @KushalKumar debounce 如何是解决此问题的适当解决方案?对于仅一次的情况,什么速率是适当的? @KushalKumar 我的意思是,这与速度无关。要求是“按钮只能被点击一次”。这就是为什么我不认为 debouce 是适合这项工作的工具。【参考方案2】:测试为工作之一:http://codepen.io/zvona/pen/KVbVPQ
class UploadArea extends React.Component
constructor(props)
super(props)
this.state =
isButtonDisabled: false
uploadFile()
// first set the isButtonDisabled to true
this.setState(
isButtonDisabled: true
);
// then do your thing
render()
return (
<button
type='submit'
onClick=() => this.uploadFile()
disabled=this.state.isButtonDisabled>
Upload
</button>
)
ReactDOM.render(<UploadArea />, document.body);
【讨论】:
这不会解决问题,因为状态更新会被 React 反跳。正因为如此,this.state.isButtonDisabled
总是会延迟获得“假”值。快速连续单击两次仍会注册 2 个 onClick 事件。
@Awol 提出了一个很好的观点,this.setState() 的批处理会导致双击仍然发生。
根据组件的复杂性,它应该足够快,可以双击,并且比使用 refs 添加属性要好得多。
@Awol React 保证交互事件(例如点击)中的 setState 在浏览器事件边界处刷新。请参阅下面的答案。如果您在事件处理程序中更改读取或设置状态,这将不是问题。
@cquezel,我不知道这个。今天学了点儿新东西。很好的发现,谢谢!【参考方案3】:
解决方案是在进入处理程序后立即检查状态。 React 保证交互事件(例如点击)内部的 setState 在浏览器事件边界处刷新。参考:https://github.com/facebook/react/issues/11171#issuecomment-357945371
// In constructor
this.state =
disabled : false
;
// Handler for on click
handleClick = (event) =>
if (this.state.disabled)
return;
this.setState(disabled: true);
// Send
// In render
<button onClick=this.handleClick disabled=this.state.disabled ...>
this.state.disabled ? 'Sending...' : 'Send'
<button>
【讨论】:
这是最干净的方法,应该是公认的答案。 我也相信和@RBT一样,这是最干净的方式,我们也在项目中这样做。 :) @cquezel 我知道每个按钮都有自己的处理程序,但this.state.disabled
对于所有按钮都是一样的!不是吗?这就是为什么当我单击其中一个按钮时它禁用了我所有的按钮。我只想禁用我点击的那个按钮。
@cquezel 您的答案非常适合单个按钮。
@ZeeshanAhmadKhalil "this.state" 对于每个按钮都不同。这就是“这个”的全部意义所在。 “this”表示每个单独对象的状态。【参考方案4】:
如果您愿意,请阻止提交。
用lodash.js debounce
怎么样
将突发事件(如击键)组合成一个事件。
https://lodash.com/docs/4.17.11#debounce
<Button accessible=true
onPress=_.debounce(async () =>
await this.props._selectUserTickets(this.props._accountId)
, 1000)
></Button>
【讨论】:
【参考方案5】:您可以尝试使用React Hooks 来设置Component State。
import React, useState from 'react';
const Button = () =>
const [double, setDouble] = useState(false);
return (
<button
disabled=double
onClick=() =>
// doSomething();
setDouble(true);
/>
);
;
export default Button;
确保您使用的是^16.7.0-alpha.x
或更高版本的react
和react-dom
。
希望对你有帮助!
【讨论】:
你的意思是useState
这个按钮会永远保持禁用状态,或者直到页面被刷新或组件被渲染?这似乎不太理想?
这不会立即禁用按钮。您依赖于 setter 来生效,这需要重新绘制组件,因此它不会是即时的。【参考方案6】:
const once = (f, g) =>
let done = false;
return (...args) =>
if (!done)
done = true;
f(...args);
else
g(...args);
;
;
const exampleMethod = () => console.log("exampleMethod executed for the first time");
const errorMethod = () => console.log("exampleMethod can be executed only once")
let onlyOnce = once(exampleMethod, errorMethod);
onlyOnce();
onlyOnce();
输出
exampleMethod executed for the first time
exampleMethod can be executed only once
【讨论】:
【参考方案7】:如果你在 onClick 期间禁用按钮,你基本上会得到这个。一个干净的方法是:
import React, useState from 'react';
import Button from '@material-ui/core/Button';
export default function CalmButton(props)
const [executing, setExecuting] = useState(false);
const
disabled,
onClick,
...otherProps
= props;
const onRealClick = async (event) =>
setExecuting(true);
try
await onClick();
finally
setExecuting(false);
;
return (
<Button
onClick=onRealClick
disabled=executing || disabled
...otherProps
/>
)
在此处查看实际操作:https://codesandbox.io/s/extended-button-that-disabled-itself-during-onclick-execution-mg6z8
我们基本上扩展了 Button 组件,增加了在 onClick 执行期间被禁用的额外行为。执行此操作的步骤:
-
如果我们正在执行,则创建要捕获的本地状态
提取我们篡改的属性(禁用,onClick)
通过设置执行状态扩展 onClick 操作
使用我们覆盖的 onClick 和扩展禁用来呈现按钮
注意:您应该确保原始的 onClick 操作是异步的,也就是它返回一个 Promise。
【讨论】:
这是一个非常干净的方法。但一件重要的事情:异步任务的持续时间需要高于 600/1000 毫秒!!!为了确保它一直有效,在“await onClick();”之后添加“await sleep(1000)” . sleep 记录在原始示例中 为什么 600/1000ms 最小?如果运行时间更短会怎样? 如果小于 600/1000 毫秒,则 someOperation()(在您的示例中)双击运行两次。但这完全正常,因为之前会检测到第二次点击。如果我在您的示例中更改“等待睡眠(1000);”,这可以很容易地复制。 '等待睡眠(10);' 但是 ClamButton 还是不错的,我在我的工具集中添加了它:) 如何防止按钮被点击一次?【参考方案8】:通过使用 event.target
,您可以禁用单击的按钮。
在创建和调用函数onClick
时使用箭头函数。不要忘记在参数中传递事件。
见我的codePen
代码如下:
class Buttons extends React.Component
constructor(props)
super(props)
this.buttons = ['A','B','C','D']
disableOnclick = (e) =>
e.target.disabled = true
render()
return(
<div>
this.buttons.map((btn,index) => (
<button type='button'
key=index
onClick=(e)=>this.disableOnclick(e)
>btn</button>
))
</div>
)
ReactDOM.render(<Buttons />, document.body);
【讨论】:
【参考方案9】:您可以在 onClick 回调和setAttribute
中获取元素引用,例如:
<Button
onClick=(e) =>
e.target.setAttribute("disabled", true);
this.handler();
>
Submit
</Button>
【讨论】:
这似乎不是 Reacty 的方法。让我想起了老式的 JS 开发。【参考方案10】:保持简单和内联:
<button type="submit"
onClick=event => event.currentTarget.disabled = true>
save
</button>
但是!当表单校准失败时,这也会禁用按钮!所以你将无法重新提交。
在这种情况下,setter 更好。
此修复此设置在表单的onSubmit
中禁用:
// state variable if the form is currently submitting
const [submitting, setSubmitting] = useState(false);
// ...
return (
<form onSubmit=e =>
setSubmitting(true); // create a method to modify the element
>
<SubmitButton showLoading=submitting>save</SubmitButton>
</form>
);
按钮看起来像这样:
import ReactComponent as IconCog from '../../img/icon/cog.svg';
import useEffect, useRef from "react";
export const SubmitButton = (children, showLoading) =>
const submitButton = useRef();
useEffect(() =>
if (showLoading)
submitButton.current.disabled = true;
else
submitButton.current.removeAttribute("disabled");
, [showLoading]);
return (
<button type="submit"
ref=submitButton>
<main>
<span>children</span>
</main>
</button>
);
;
【讨论】:
以上是关于ReactJs:防止多次按下按钮的主要内容,如果未能解决你的问题,请参考以下文章