ReactJs: Prevent multiple times button press

JavascriptReactjs

Javascript Problem Overview


In my React component I have a button meant to send some data over AJAX when clicked. I need to happen only the first time, i.e. to disable the button after its first use.

How I'm trying to do this:

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>
    );
  }

});

What I think happens is the state variable showUploadButton not being updated right away, which the React docs says is expected.

How could I enforce the button to get disabled or go away altogether the instant it's being clicked?

Javascript Solutions


Solution 1 - Javascript

The solution is to check the state immediately upon entry to the handler. React guarantees that setState inside interactive events (such as click) is flushed at browser event boundary. Ref: 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>

Solution 2 - Javascript

What you could do is make the button disabled after is clicked and leave it in the page (not clickable element).

To achieve this you have to add a ref to the button element

<button ref="btn" onClick={this.onClickUploadFile}>Send</button>

and then on the onClickUploadFile function disable the button

this.refs.btn.setAttribute("disabled", "disabled");

You can then style the disabled button accordingly to give some feedback to the user with

.btn:disabled{ /* styles go here */}

If needed make sure to reenable it with

this.refs.btn.removeAttribute("disabled");

Update: the preferred way of handling refs in React is with a function and not a string.

<button 
  ref={btn => { this.btn = btn; }} 
  onClick={this.onClickUploadFile}
>Send</button>


this.btn.setAttribute("disabled", "disabled");
this.btn.removeAttribute("disabled");

Update: Using react hooks

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>

here is a small example using the code you provided https://jsfiddle.net/69z2wepo/30824/

Solution 3 - Javascript

Tested as working one: 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);

Solution 4 - Javascript

You can try using React Hooks to set the 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;

Make sure you are using ^16.7.0-alpha.x version or later of react and react-dom.

Hope this helps you!

Solution 5 - Javascript

If you disable the button during onClick, you basically get this. A clean way of doing this would be:

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}
        />
    )
}

See it in action here: https://codesandbox.io/s/extended-button-that-disabled-itself-during-onclick-execution-mg6z8

We basically extend the Button component with the extra behaviour of being disabled during onClick execution. Steps to do this:

  1. Create local state to capture if we are executing
  2. Extract properties we tamper with (disabled, onClick)
  3. Extend onClick operation with setting the execution state
  4. Render the button with our overridden onClick, and extended disabled

NOTE: You should ensure that the original onClick operation is async aka it is returning a Promise.

Solution 6 - Javascript

If you want, just prevent to submit.

How about using lodash.js debounce

Grouping a sudden burst of events (like keystrokes) into a single one.

https://lodash.com/docs/4.17.11#debounce

<Button accessible={true}
    onPress={_.debounce(async () => {
                await this.props._selectUserTickets(this.props._accountId)
    }, 1000)}
></Button>

Solution 7 - Javascript

By using event.target , you can disabled the clicked button. Use arrow function when you create and call the function onClick. Don't forget to pass the event in parameter.

See my codePen

Here is the code:

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);

Solution 8 - Javascript

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();

output

exampleMethod executed for the first time
exampleMethod can be executed only once

Solution 9 - Javascript

You can get the element reference in the onClick callback and setAttribute from there, eg:

      <Button
        onClick={(e) => {
          e.target.setAttribute("disabled", true);
          this.handler();
        }}            
      >
        Submit
      </Button>

Solution 10 - Javascript

Keep it simple and inline:

<button type="submit"
        onClick={event => event.currentTarget.disabled = true}>
    save
</button>

But! This will also disable the button, when the form calidation failed! So you will not be able to re-submit.

In this case a setter is better.

This fix this set the disabled in the onSubmit of the form:


// 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>
);

And the button would look like this:

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>
    );

};

Solution 11 - Javascript

Another approach could be like so:

<button onClick={this.handleClick} disabled={isLoading ? "disabled" :""}>Send</button>

Solution 12 - Javascript

My approach is if event on processing do not execute anything.

class UploadArea extends React.Component {
constructor(props) {
super(props)

this.state = {
  onProcess:false
   }
}

uploadFile() {
 if (!this.state.onProcess){
   this.setState({
     onProcess: true
   });
   // then do your thing
   this.setState({
     onProcess: false;
   });
 }    
}

render() {
  return (
    <button
      type='submit'
      onClick={() => this.uploadFile()}>
      Upload
    </button>
   )
  }
}

ReactDOM.render(<UploadArea />, document.body);

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionIulius CurtView Question on Stackoverflow
Solution 1 - JavascriptcquezelView Answer on Stackoverflow
Solution 2 - JavascripteltonkamamiView Answer on Stackoverflow
Solution 3 - JavascriptSamuli HakoniemiView Answer on Stackoverflow
Solution 4 - JavascriptsaetaView Answer on Stackoverflow
Solution 5 - JavascriptVajk HermeczView Answer on Stackoverflow
Solution 6 - JavascriptjunhoView Answer on Stackoverflow
Solution 7 - JavascriptGazowskiView Answer on Stackoverflow
Solution 8 - JavascriptAshok RView Answer on Stackoverflow
Solution 9 - Javascriptrudresh solankiView Answer on Stackoverflow
Solution 10 - JavascriptHannes SchneidermayerView Answer on Stackoverflow
Solution 11 - JavascriptNgomaneView Answer on Stackoverflow
Solution 12 - JavascriptSemih Can BilgenView Answer on Stackoverflow