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 或更高版本的reactreact-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:防止多次按下按钮的主要内容,如果未能解决你的问题,请参考以下文章

使用 reactjs 和 axios 防止 json 数组重复

按下后退按钮时防止反应路由器重新加载 API 调用。反应

ReactJS - 使用反应钩子防止模态的初始动画

reactjs中如何使用按钮保存对象

reactjs中如何处理this.setState问题

解雇reactjs中的项目的问题