ReactJs: Preveniți apăsarea de mai multe ori a butonului (Programare, Javascript, Reactjs, React Jsx)

Iulius Curt a intrebat.

În componenta mea React am un buton menit să trimită niște date prin AJAX atunci când este apăsat. Am nevoie să se întâmple doar prima dată, adică să dezactivez butonul după prima utilizare a acestuia.

Cum încerc să fac acest lucru:

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

});

Ceea ce cred că se întâmplă este ca variabila de stare showUploadButton nu este actualizată imediat, ceea ce în documentele React se spune că este de așteptat.

Cum aș putea impune ca butonul să fie dezactivat sau să dispară cu totul în momentul în care este apăsat?

Comentarii

  • Ați observat vreo problemă sau doar vă întrebați? Ați reușit să trimiteți de două ori? –  > Por cquezel.
  • Biblioteca Formik se ocupă în mod implicit de această problemă, căutați despre issubmitting pe site-ul Formik –  > Por Mohammad.
  • Vă rugăm să vă uitați la răspunsul lui @cquezel pentru o abordare mai curată. Dezactivarea controalelor de formular cu ref (prezentată în răspunsul acceptat) este o chestie veche și este posibil să fi fost relevantă în versiunile inițiale ale ReactJs. –  > Por RBT.
9 răspunsuri
eltonkamami

Ceea ce ați putea face este să faceți butonul dezactivat după ce este făcut clic și să-l lăsați în pagină (element care nu poate fi făcut clic).

Pentru a realiza acest lucru trebuie să adăugați un ref la elementul buton

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

și apoi pe funcția onClickUploadFile să dezactivați butonul

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

Puteți apoi stiliza butonul dezactivat în mod corespunzător pentru a oferi un feedback utilizatorului cu

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

Dacă este necesar, asigurați-vă că îl reactivați cu

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

Actualizare: modul preferat de gestionare a ref-urilor în React este cu o funcție și nu cu un șir de caractere.

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


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

Actualizare: Utilizarea cârligelor react

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>

iată un mic exemplu care folosește codul pe care l-ați furnizathttps://jsfiddle.net/69z2wepo/30824/

Comentarii

  • Acest lucru m-a dus la jumătatea drumului, dar echipa React a renunțat să mai dea ref o valoare de șir de caractere, și în schimb folosesc cu ea un callback: reactjs.org/docs/refs-and-the-dom.html –  > Por Martin.
  • Îmi dă o eroare ‘TypeError: self.btn.setAttribute is not a function’ 🙁 – –  > Por Kushal Kumar.
  • Cel mai bun anser este debounce –  > Por Kushal Kumar.
  • @KushalKumar Cum este debounce o soluție adecvată pentru această problemă și ce rată ar fi adecvată pentru un scenariu doar o singură dată? –  > Por cquezel.
  • @KushalKumar Ideea mea este că acest lucru nu are nimic de-a face cu viteza. Cerința este „butonul poate fi apăsat o singură dată”. De aceea, nu cred că debouce este instrumentul potrivit pentru această sarcină. –  > Por cquezel.
cquezel

Soluția este de a verifica starea imediat la intrarea în handler. React garantează că setState în interiorul evenimentelor interactive (cum ar fi click) este curățat la limita evenimentului din browser. 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>

Comentarii

  • Aceasta este cea mai curată abordare și ar trebui să fie răspunsul acceptat. –  > Por RBT.
  • Și eu cred la fel ca @RBT, acesta este cel mai curat mod de a proceda și noi procedăm la fel și în cadrul proiectelor. 🙂 –  > Por Imran Rafiq Rather.
  • @cquezel înțeleg că fiecare buton va avea propriul handler, dar this.state.disabled este același pentru toate butoanele! nu-i așa? De aceea mi-a dezactivat toate butoanele atunci când am dat click pe unul dintre ele. Am vrut să dezactivez doar acel buton pe care am făcut clic. –  > Por Zeeshan Ahmad Khalil.
  • @cquezel răspunsul tău este perfect pentru un singur buton. –  > Por Zeeshan Ahmad Khalil.
  • @ZeeshanAhmadKhalil „this.state” este diferit pentru fiecare buton. Despre asta este vorba în „this”. „this” reprezintă starea fiecărui obiect individual. –  > Por cquezel.
Samuli Hakoniemi

Testat ca fiind unul de lucru: 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);

Comentarii

  • Acest lucru nu ar rezolva problema, deoarece actualizările de stare sunt dezactivate de React. Din acest motiv, ar exista întotdeauna o întârziere în this.state.isButtonDisabled pentru a obține valoarea „false”. Dacă ați face clic de două ori în succesiune rapidă, tot s-ar înregistra 2 evenimente onClick. –  > Por Awol.
  • @Awol are o observație foarte bună: punerea în loturi a this.setState() face ca clicurile duble să se producă în continuare. –  > Por ksloan.
  • în funcție de complexitatea componentei, ar trebui să fie suficient de rapid pentru un dublu clic și este de departe un design mai bun decât adăugarea unui atribut folosind refs. –  > Por Achim Koellner.
  • @Awol React garantează că setState în interiorul evenimentelor interactive (cum ar fi click) este curățat la limita evenimentului din browser. consultați răspunsul meu de mai jos. Dacă modificați starea de citire sau de setare într-un gestionar de evenimente, aceasta nu va fi o problemă. –  > Por cquezel.
  • @cquezel, nu știam acest lucru. Azi am învățat ceva nou. Bună descoperire, mulțumesc! –  > Por Awol.
slorenzo

Puteți încerca să utilizați React Hooks pentru a seta starea componentei.

import React, { useState } from 'react';

const Button = () => {
  const [double, setDouble] = useState(false);
  return (
    <button
      disabled={double}
      onClick={() => {
        // doSomething();
        setDouble(true);
      }}
    />
  );
};

export default Button;

Asigurați-vă că utilizați ^16.7.0-alpha.x versiunea de react și react-dom.

Sper că vă ajută!

Comentarii

  • vrei să spui useState –  > Por lukas1994.
Vajk Hermecz

Dacă dezactivați butonul în timpul onClick, practic veți obține acest lucru. O modalitate curată de a face acest lucru ar fi:

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

Vedeți-o în acțiune aici: https://codesandbox.io/s/extended-button-that-disabled-itself-during-onclick-execution-mg6z8

Practic, extindem componenta Button cu comportamentul suplimentar de a fi dezactivată în timpul execuției onClick. Pași pentru a face acest lucru:

  1. Crearea unei stări locale pentru a capta dacă executăm
  2. Extragem proprietățile pe care le modificăm (disabled, onClick)
  3. Extindeți operațiunea onClick cu setarea stării de execuție
  4. Redați butonul cu onClick modificat și cu disabled extins.

NOTĂ: Trebuie să vă asigurați că operațiunea onClick originală este asincronă, adică returnează o promisiune.

Comentarii

  • Aceasta este o abordare foarte curată. Dar un lucru important: durata sarcinii asincrone trebuie să fie mai mare de 600/1000 ms!!! Pentru a fi siguri că funcționează tot timpul, adăugați „await sleep(1000)” după „await onClick();” . sleep este documentat în exemplul original –  > Por Chris.
  • De ce este minimul de 600/1000 ms? Ce se întâmplă dacă timpul de execuție este mai scurt? –  > Por Vajk Hermecz.
  • dacă este mai mică de 600/1000ms, atunci someOperation() (în exemplul dvs.) se execută de două ori la un dublu clic. Dar acest lucru este perfect normal, deoarece al doilea clic va fi detectat înainte. Acest lucru poate fi ușor de reprodus dacă schimb în exemplul dvs. „await sleep(1000);” „cu await sleep(10);”.  > Por Chris.
  • dar, din nou, ClamButton este frumos, l-am adăugat în setul meu de instrumente 🙂 –  > Por Chris.
junho

Dacă doriți, doar împiedicați să trimiteți.

Ce-ar fi să folosiți lodash.js debounce

Gruparea unei explozii bruște de evenimente (cum ar fi apăsarea tastelor) într-unul singur.

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

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

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

ieșire

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

Gazowski

Prin utilizarea event.target , puteți dezactiva butonul pe care s-a făcut clic.Utilizați funcția săgeată atunci când creați și apelați funcția onClick. Nu uitați să treceți evenimentul în parametru.

Vezi pagina mea codePen

Aici este codul:

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

rudresh solanki

Puteți obține referința elementului în callback-ul onClick și setAttribute de acolo, de exemplu:

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

Comentarii

  • Acest lucru nu pare a fi modul Reacty de a face acest lucru. Îmi amintește de dezvoltarea JS de școală veche. –  > Por Fusion.