score:58

Accepted answer

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/

score:0

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

};

score:0

Another approach could be like so:

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

score:0

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

score:1

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

score:1

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>

score:2

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

score:5

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>

score:6

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.

score:9

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!

score:21

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

score:71

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>

Related Query

More Query from same tag