Efectuați debounce în React.js (Programare, Javascript, Reactjs)

Chetan Ankola a intrebat.

Cum efectuați debounce în React.js?

Vreau să debounce handleOnChange.

Am încercat cu debounce(this.handleOnChange, 200) dar nu funcționează.

function debounce(fn, delay) {
  var timer = null;
  return function() {
    var context = this,
      args = arguments;
    clearTimeout(timer);
    timer = setTimeout(function() {
      fn.apply(context, args);
    }, delay);
  };
}

var SearchBox = React.createClass({
  render: function() {
    return <input type="search" name="p" onChange={this.handleOnChange} />;
  },

  handleOnChange: function(event) {
    // make ajax call
  }
});

Comentarii

  • Am întâlnit aceeași problemă cu tine, răspunsuri superbe mai jos!dar cred că ai folosit un mod greșit de debounce. aici, când onChange={debounce(this.handleOnChange, 200)}/>, se va invoca debounce function de fiecare dată. dar ,de fapt, ceea ce avem nevoie este să invocăm funcția pe care a returnat-o funcția debounce. –  > Por pingfengafei.
38 răspunsuri
Sebastien Lorber

2019: Încercați cârlige + promisiune de debouncing

Aceasta este cea mai actualizată versiune a modului în care aș rezolva această problemă. Aș folosi:

Aceasta este o anumită cablare inițială, dar compuneți blocuri primitive pe cont propriu și vă puteți face propriul cârlig personalizat, astfel încât să nu trebuie să faceți acest lucru decât o singură dată.

// Generic reusable hook
const useDebouncedSearch = (searchFunction) => {

  // Handle the input text state
  const [inputText, setInputText] = useState('');

  // Debounce the original search async function
  const debouncedSearchFunction = useConstant(() =>
    AwesomeDebouncePromise(searchFunction, 300)
  );

  // The async callback is run each time the text changes,
  // but as the search function is debounced, it does not
  // fire a new request on each keystroke
  const searchResults = useAsync(
    async () => {
      if (inputText.length === 0) {
        return [];
      } else {
        return debouncedSearchFunction(inputText);
      }
    },
    [debouncedSearchFunction, inputText]
  );

  // Return everything needed for the hook consumer
  return {
    inputText,
    setInputText,
    searchResults,
  };
};

Și apoi puteți utiliza cârligul dvs:

const useSearchStarwarsHero = () => useDebouncedSearch(text => searchStarwarsHeroAsync(text))

const SearchStarwarsHeroExample = () => {
  const { inputText, setInputText, searchResults } = useSearchStarwarsHero();
  return (
    <div>
      <input value={inputText} onChange={e => setInputText(e.target.value)} />
      <div>
        {searchResults.loading && <div>...</div>}
        {searchResults.error && <div>Error: {search.error.message}</div>}
        {searchResults.result && (
          <div>
            <div>Results: {search.result.length}</div>
            <ul>
              {searchResults.result.map(hero => (
                <li key={hero.name}>{hero.name}</li>
              ))}
            </ul>
          </div>
        )}
      </div>
    </div>
  );
};

Veți găsi acest exemplu care rulează aici și ar trebui să citiți react-async-hook pentru mai multe detalii.


2018: încercați promisiunea de debouncing

Deseori dorim să debounțăm apelurile API pentru a evita inundarea backend-ului cu cereri inutile.

În 2018, lucrul cu callback-uri (Lodash/Underscore) mi se pare rău și predispus la erori. Este ușor să întâmpinăm probleme de boilerplate și de concurență din cauza apelurilor API care se rezolvă într-o ordine arbitrară.

Am creat o mică bibliotecă cu React în minte pentru a vă rezolva durerile: awesome-debounce-promise.

Acest lucru nu ar trebui să fie mai complicat decât atât:

const searchAPI = text => fetch('/search?text=' + encodeURIComponent(text));

const searchAPIDebounced = AwesomeDebouncePromise(searchAPI, 500);

class SearchInputAndResults extends React.Component {
  state = {
    text: '',
    results: null,
  };

  handleTextChange = async text => {
    this.setState({ text, results: null });
    const result = await searchAPIDebounced(text);
    this.setState({ result });
  };
}

Funcția debounced se asigură că:

  • Apelurile API vor fi deblocate
  • funcția debounced returnează întotdeauna o promisiune
  • doar promisiunea returnată de ultimul apel va fi rezolvată
  • o singură this.setState({ result }); se va întâmpla pentru fiecare apel API

În cele din urmă, este posibil să adăugați un alt truc dacă componenta dvs. se demontează:

componentWillUnmount() {
  this.setState = () => {};
}

Rețineți că Observables (RxJS) pot fi, de asemenea, foarte potrivite pentru deblocarea intrărilor, dar este o abstracțiune mai puternică, care poate fi mai greu de învățat/utilizat corect.


< 2017: Încă mai doriți să folosiți debouncing callback?

Partea importantă aici este să creați o singură funcție debounced (sau throttled) pentru fiecare instanță de componentă. Nu doriți să recreați funcția debounce (sau throttle) de fiecare dată și nu doriți nici ca mai multe instanțe să împartă aceeași funcție debounce.

Nu definesc o funcție de debouncing în acest răspuns, deoarece nu este cu adevărat relevant, dar acest răspuns va funcționa perfect cu _.debounce de underscore sau lodash, precum și cu orice funcție de debouncing furnizată de utilizator.


O IDEE BUNĂ:

Deoarece funcțiile debounced sunt cu stare, trebuie să creăm o funcție de debounced pentru fiecare instanță de componentă.

ES6 (proprietate de clasă): recomandat

class SearchBox extends React.Component {
    method = debounce(() => { 
      ...
    });
}

ES6 (constructor de clasă)

class SearchBox extends React.Component {
    constructor(props) {
        super(props);
        this.method = debounce(this.method.bind(this),1000);
    }
    method() { ... }
}

ES5

var SearchBox = React.createClass({
    method: function() {...},
    componentWillMount: function() {
       this.method = debounce(this.method.bind(this),100);
    },
});

A se vedea JsFiddle: 3 instanțe produc câte o intrare în jurnal pentru fiecare instanță (ceea ce înseamnă 3 la nivel global).


NU este o idee bună:

var SearchBox = React.createClass({
  method: function() {...},
  debouncedMethod: debounce(this.method, 100);
});

Nu va funcționa, deoarece în timpul creării obiectului de descriere a clasei, this nu este obiectul creat în sine. this.method nu returnează ceea ce vă așteptați, deoarece this context nu este obiectul în sine (care, de fapt, încă nu există cu adevărat BTW, deoarece este doar în curs de creare).


NU este o idee bună:

var SearchBox = React.createClass({
  method: function() {...},
  debouncedMethod: function() {
      var debounced = debounce(this.method,100);
      debounced();
  },
});

De data aceasta creați efectiv o funcție debifată care apelează funcția dvs. this.method. Problema este că o recreați la fiecare debouncedMethod apel, astfel încât funcția debounce nou creată nu știe nimic despre apelurile anterioare! Trebuie să refolosiți aceeași funcție deboununced de-a lungul timpului, altfel debuncing-ul nu va avea loc.


NU este o idee bună:

var SearchBox = React.createClass({
  debouncedMethod: debounce(function () {...},100),
});

Acest lucru este un pic mai complicat aici.

Toate instanțele montate ale clasei vor împărți aceeași funcție debounced și, de cele mai multe ori, acest lucru nu este ceea ce vă doriți!. A se vedea JsFiddle: 3 instanțe produc doar o singură intrare în jurnal la nivel global.

Trebuie să creați o funcție de debounced pentru fiecare instanță componentă, și nu o singură funcție debounced la nivel de clasă, partajată de fiecare instanță de componentă.


Aveți grijă de centralizarea evenimentelor din React

Acest lucru este legat de faptul că deseori dorim să debranșăm sau să reglăm evenimentele DOM.

În React, obiectele de eveniment (de ex, SyntheticEvent) pe care le primiți în callback-uri sunt puse în comun (acesta este acum documentat). Acest lucru înseamnă că, după ce apelul de eveniment a fost apelat, SyntheticEvent pe care îl primiți va fi pus înapoi în pool cu atribute goale pentru a reduce presiunea GC.

Prin urmare, dacă accesați SyntheticEvent proprietățile în mod asincron la callback-ul original (așa cum ar putea fi cazul dacă accelerați/debutați), proprietățile pe care le accesați pot fi șterse. Dacă doriți ca evenimentul să nu fie niciodată repus în pool, puteți utiliza atributul persist() metoda .

Fără persistență (comportament implicit: eveniment pus în comun)

onClick = e => {
  alert(`sync -> hasNativeEvent=${!!e.nativeEvent}`);
  setTimeout(() => {
    alert(`async -> hasNativeEvent=${!!e.nativeEvent}`);
  }, 0);
};

A 2-a (async) va imprima hasNativeEvent=false deoarece proprietățile evenimentului au fost curățate.

Cu persist

onClick = e => {
  e.persist();
  alert(`sync -> hasNativeEvent=${!!e.nativeEvent}`);
  setTimeout(() => {
    alert(`async -> hasNativeEvent=${!!e.nativeEvent}`);
  }, 0);
};

Al doilea (async) se va imprima hasNativeEvent=true deoarece persist vă permite să evitați să puneți evenimentul înapoi în fondul de evenimente.

Puteți testa aceste două comportamente aici: JsFiddle

Citiți răspunsul lui Julen pentru un exemplu de utilizare a persist() cu o funcție de accelerare/retur.

Comentarii

  • Superb răspuns, acest lucru este excelent pentru a seta starea câmpurilor de formular ca fiind „interactivă” pentru câteva secunde după ce se opresc din tastare, și apoi să poată fi anulată la trimiterea formularului sau la onBlur –  > Por arush_try.com.
  • Rețineți că în ES6, în loc să definiți metoda în interiorul constructorului (pare ciudat), puteți face handleOnChange = debounce((e) => { /* onChange handler code here */ }, timeout) la nivelul superior al clasei dumneavoastră. Încă setați efectiv un membru de instanță, dar arată puțin mai mult ca o definiție normală a unei metode. Nu mai este nevoie de un constructor dacă nu aveți deja una definită. Presupun că este mai ales o preferință de stil. –  > Por thom_nic.
  • 27

  • Nu uitați să anulați metoda debounced în componentWillUnmount: this.method.cancel() – altfel s-ar putea să vrea să setState pe o componentă nemontată. –  > Por elado.
  • @JonasKello nu poți debloca în interiorul unei componente fără stare, deoarece funcția deblocată este de fapt cu stare. Aveți nevoie de o componentă cu stare pentru a deține acea funcție debounced, dar puteți apela o componentă fără stare cu o funcție deja debounced, dacă este necesar. –  > Por Sebastien Lorber.
  • De ce toate răspunsurile includ _.debounce în loc să scrie funcția ? Este nevoie de întreaga bibliotecă pentru acea funcție ? –  > Por chifliiiii.
julen

Componente necontrolate

Puteți utiliza event.persist() metoda.

Urmează un exemplu în care se folosește underscore’s _.debounce():

var SearchBox = React.createClass({

  componentWillMount: function () {
     this.delayedCallback = _.debounce(function (event) {
       // `event.target` is accessible now
     }, 1000);
  },

  onChange: function (event) {
    event.persist();
    this.delayedCallback(event);
  },

  render: function () {
    return (
      <input type="search" onChange={this.onChange} />
    );
  }

});

Editare: A se vedea acest JSFiddle


Componente controlate

Actualizare: Exemplul de mai sus arată un componentă necontrolată. Eu folosesc tot timpul elemente controlate, așa că iată un alt exemplu de mai sus, dar fără a folosi elementul event.persist() „șmecherie”.

A JSFiddle este disponibil de asemenea. Exemplu fără subliniere

var SearchBox = React.createClass({
    getInitialState: function () {
        return {
            query: this.props.query
        };
    },

    componentWillMount: function () {
       this.handleSearchDebounced = _.debounce(function () {
           this.props.handleSearch.apply(this, [this.state.query]);
       }, 500);
    },

    onChange: function (event) {
      this.setState({query: event.target.value});
      this.handleSearchDebounced();
    },

    render: function () {
      return (
        <input type="search"
               value={this.state.query}
               onChange={this.onChange} />
      );
    }
});


var Search = React.createClass({
    getInitialState: function () {
        return {
            result: this.props.query
        };
    },

    handleSearch: function (query) {
        this.setState({result: query});
    },

    render: function () {
      return (
        <div id="search">
          <SearchBox query={this.state.result}
                     handleSearch={this.handleSearch} />
          <p>You searched for: <strong>{this.state.result}</strong></p>
        </div>
      );
    }
});

React.render(<Search query="Initial query" />, document.body);

Editare: exemplele și JSFiddles actualizate la React 0.12

Editare: exemple actualizate pentru a rezolva problema ridicată de Sebastien Lorber

Edit: actualizat cu JSfiddle care nu folosește underscore și folosește simplu debounce javascript.

Comentarii

  • Acest lucru nu funcționează pentru intrări. Ținta evenimentului din funcția debounced nu mai are o valoare… așa că intrarea rămâne goală. –  > Por Etai.
  • Ușor complexă, aceasta. Trebuie să fiți puțin atenți la props. Dacă setați <input value={this.props.someprop}... atunci nu se va reda corect, deoarece actualizarea la apăsarea tastelor nu ajunge înapoi în componentă decât după debounce. Este bine să omiteți value= dacă vă mulțumiți ca acest lucru să nu fie gestionat, dar dacă doriți să precompletați valoarea și/sau să o legați în altă parte, atunci, evident, acest lucru nu funcționează. –  > Por Alastair Maw.
  • @AlastairMaw întrebarea avea o componentă necontrolată, de aceea și răspunsul o are. Am adăugat mai jos o versiune alternativă pentru componente controlate, cu o valoare preumplută. –  > Por julen.
  • acest lucru este foarte periculos dacă montați componenta de mai multe ori în DOM, vezi stackoverflow.com/questions/23123138/… –  > Por Sebastien Lorber.
  • în timp ce acesta este un răspuns excelent, nu vă recomand să folosiți persist mai ales atunci când pot exista o mulțime de evenimente, cum ar fi pe mousemove. Am văzut coduri care au devenit total insensibile în acest mod. Este mult mai eficient să extrageți datele necesare din evenimentul nativ în apelul de eveniment și apoi să apelați funcția debounced / throttled doar cu datele, NU cu evenimentul în sine. Nu este nevoie să persistați evenimentul în acest mod –  > Por MrE.
Sameer Ingavale

2019: Utilizați cârligul react hook „useCallback

După ce am încercat mai multe abordări diferite, am constatat că utilizarea useCallback să fie cea mai simplă și cea mai eficientă pentru a rezolva problema apelurilor multiple a utilizării debounce în cadrul unui onChange eveniment.

În conformitate cu documentația API Hooks,

useCallback returnează o versiune memorată a callback-ului care se modifică numai dacă una dintre dependențe s-a schimbat.

Dacă se trece o matrice goală ca dependență, se asigură că callback-ul este apelat doar o singură dată. Iată o implementare simplă :

import React, { useCallback } from "react";
import { debounce } from "lodash";

const handler = useCallback(debounce(someFunction, 2000), []);

const onChange = (event) => {
    // perform any event related action here

    handler();
 };

Sper că vă ajută!

Comentarii

  • O soluție excelentă dacă folosiți cârlige. M-ai salvat de multe alte ore de frustrare. Mulțumesc! –  > Por Carl Edwards.
  • Ați putea să explicați de ce se întâmplă apelurile multiple în primul rând? Are debounce() nu ia în considerare onChange() callback să fie aceeași metodă de callback? –  > Por El Anonimo.
  • Am modificat această soluție pentru a o face să funcționeze în aplicația mea. Mai întâi a trebuit să mut linia const testFunc2 = useCallback(debounce((text) => console.log('testFunc2() has ran:', text), 1000) , []); în interiorul corpului componentei funcției, altfel React emite un mesaj de eroare despre utilizarea cârligului în afara acesteia. Apoi, în onChange event handler: <input type='text' name='name' className='th-input-container__input' onChange={evt => {testFunc2(evt.target.value);}}. –  > Por El Anonimo.
  • Iată cum am folosit această soluție pentru a lăsa utilizatorul să tasteze la o intrare, apoi să trimită un apel API cu valoarea de intrare după ce a terminat de tastat. stackoverflow.com/questions/59358092/…. –  > Por El Anonimo.
  • Adăugând la răspunsul de mai sus —- const someFunction = (text) => { dispatch({ type: „addText”, payload: { id, text, }, }, }); }; }; <input type=”text” defaultValue={text} onChange={(e) => handler(e.target.value)} /> –  > Por Subhadip Pal.
Hooman Askari

După ce m-am luptat cu intrările de text pentru o vreme și nu am găsit o soluție perfectă pe cont propriu, am găsit acest lucru pe npm: react-debounce-input.

Iată un exemplu simplu:

import React from 'react';
import ReactDOM from 'react-dom';
import {DebounceInput} from 'react-debounce-input';

class App extends React.Component {
state = {
    value: ''
};

render() {
    return (
    <div>
        <DebounceInput
        minLength={2}
        debounceTimeout={300}
        onChange={event => this.setState({value: event.target.value})} />

        <p>Value: {this.state.value}</p>
    </div>
    );
}
}

const appRoot = document.createElement('div');
document.body.appendChild(appRoot);
ReactDOM.render(<App />, appRoot);

Componenta DebounceInput acceptă toate accesoriile pe care le puteți atribui unui element de intrare normal. Încercați-o pe codepen

Sper să ajute și pe altcineva și să economisească timp.

Comentarii

  • După ce am încercat multe soluții enumerate aici, cu siguranță a fost cea mai ușoară. –  > Por Vadorequest.
  • Aceasta este într-adevăr o soluție mult mai bună! Nu doar pentru că folosește cea mai mică cantitate de cod, ci și pentru că permite deblocarea funcțiilor de clasă (spre deosebire de awesome-debounce-promise, care este aproape inutilă din acest motiv) – –  > Por dmitry.matora.
sledgeweight

Am găsit această postare de Justin Tulk foarte utilă. După câteva încercări, în ceea ce s-ar percepe ca fiind modul mai oficial cu react/redux, se arată că eșuează din cauza Pooling-ul sintetic de evenimente din React. Soluția sa folosește apoi o stare internă pentru a urmări valoarea modificată/introdusă în input, cu un callback imediat după setState care apelează o acțiune redux accelerată/debotezată care arată unele rezultate în timp real.

import React, {Component} from 'react'
import TextField from 'material-ui/TextField'
import { debounce } from 'lodash'

class TableSearch extends Component {

  constructor(props){
    super(props)

    this.state = {
        value: props.value
    }

    this.changeSearch = debounce(this.props.changeSearch, 250)
  }

  handleChange = (e) => {
    const val = e.target.value

    this.setState({ value: val }, () => {
      this.changeSearch(val)
    })
  }

  render() {

    return (
        <TextField
            className = {styles.field}
            onChange = {this.handleChange}
            value = {this.props.value}
        />
    )
  }
}

Comentarii

  • o soluție frumoasă pentru o componentă de stare. –  > Por Shoyeb Memon.
Yura

Dacă tot ceea ce aveți nevoie de la obiectul evenimentului este să obțineți elementul de intrare DOM, soluția este mult mai simplă – folosiți doar ref. Rețineți că acest lucru necesită Underscore:

class Item extends React.Component {
    constructor(props) {
        super(props);
        this.saveTitle = _.throttle(this.saveTitle.bind(this), 1000);
    }
    saveTitle(){
        let val = this.inputTitle.value;
        // make the ajax call
    }
    render() {
        return <input 
                    ref={ el => this.inputTitle = el } 
                    type="text" 
                    defaultValue={this.props.title} 
                    onChange={this.saveTitle} />
    }
}

Comentarii

  • defaultValue este ceea ce vreau eu! Vă mulțumesc foarte mach 🙂 –  > Por Tazo leladze.
Mohan Dere

Cu debounce trebuie să păstrați evenimentul sintetic original în jurul valorii de cu event.persist(). Iată un exemplu de lucru testat cu React 16+.

import React, { Component } from 'react';
import debounce from 'lodash/debounce'

class ItemType extends Component {

  evntHandler = debounce((e) => {
    console.log(e)
  }, 500);

  render() {
    return (
      <div className="form-field-wrap"
      onClick={e => {
        e.persist()
        this.evntHandler(e)
      }}>
        ...
      </div>
    );
  }
}
export default ItemType;

Cu componenta funcțională, puteți face acest lucru –

const Search = ({ getBooks, query }) => {

  const handleOnSubmit = (e) => {
    e.preventDefault();
  }
  const debouncedGetBooks = debounce(query => {
    getBooks(query);
  }, 700);

  const onInputChange = e => {
    debouncedGetBooks(e.target.value)
  }

  return (
    <div className="search-books">
      <Form className="search-books--form" onSubmit={handleOnSubmit}>
        <Form.Group controlId="formBasicEmail">
          <Form.Control type="text" onChange={onInputChange} placeholder="Harry Potter" />
          <Form.Text className="text-muted">
            Search the world's most comprehensive index of full-text books.
          </Form.Text>
        </Form.Group>
        <Button variant="primary" type="submit">
          Search
        </Button>
      </Form>
    </div>
  )
}

Referințe – – – https://gist.github.com/elijahmanor/08fc6c8468c994c844213e4a4344a709https://blog.revathskumar.com/2016/02/reactjs-using-debounce-in-react-components.html

chad steele

O mulțime de informații bune aici deja, dar pentru a fi succint. Acest lucru funcționează pentru mine…

import React, {Component} from 'react';
import _ from 'lodash';

class MyComponent extends Component{
      constructor(props){
        super(props);
        this.handleChange = _.debounce(this.handleChange.bind(this),700);
      }; 

Comentarii

  • Acest lucru nu funcționează pentru mine. Starea nu se actualizează. Dacă înlătur _debounce wrapper-ul, funcționează. Îmi place această idee totuși! –  > Por Mote Zart.
  • Ar trebui să vă văd codul pentru a oferi multe aici, dar bănuiesc că se întâmplă altceva… sper că acest răspuns mult mai amănunțit va face lumină. stackoverflow.com/questions/23123138/… –  > Por chad steele.
  • A funcționat ca un farmec pentru mine. Am înfășurat funcția handler legată ca mai sus, apoi am actualizat starea în funcția handler pe baza intrării câmpului. Mulțumesc! –  > Por user3006381.
Matt

Dacă folosiți redux, puteți face acest lucru într-un mod foarte elegant cu middleware. Puteți defini un Debounce middleware ca:

var timeout;
export default store => next => action => {
  const { meta = {} } = action;
  if(meta.debounce){
    clearTimeout(timeout);
    timeout = setTimeout(() => {
      next(action)
    }, meta.debounce)
  }else{
    next(action)
  }
}

Puteți adăuga apoi debouncing la creatorii de acțiuni, cum ar fi::

export default debouncedAction = (payload) => ({
  type : 'DEBOUNCED_ACTION',
  payload : payload,
  meta : {debounce : 300}
}

Există de fapt deja middleware pe care îl puteți obține de pe npm pentru a face acest lucru pentru dvs.

Comentarii

  • Cred că acest middleware trebuie să fie primul care să fie executat în applyMiddleware(...) lanț dacă avem mai multe –  > Por Youssef.
  • Timpul de așteptare nu este inițializat și acel prim clearTimeout va avea de-a face cu un parametru nedefinit. Nu este bine. –  > Por Jason Rice.
STEEL

Utilizarea ES6 CLASS și React 15.x.x & lodash.debounceIm folosind React’s refs aici, deoarece pierderile de evenimente pierd acest bind în mod intern.

Dinesh Madanlal Jain

Puteți utiliza Lodash debounce https://lodash.com/docs/4.17.5#debounce metoda. Este simplă și eficientă.

import * as lodash from lodash;

const update = (input) => {
    // Update the input here.
    console.log(`Input ${input}`);     
}

const debounceHandleUpdate = lodash.debounce((input) => update(input), 200, {maxWait: 200});

doHandleChange() {
   debounceHandleUpdate(input);
}

De asemenea, puteți anula metoda debounce folosind metoda de mai jos.

this.debounceHandleUpdate.cancel();

Sper că vă ajută. Noroc!!!

kenju

FYI

Iată o altă implementare PoC:

  • fără nicio bibliotecă (de exemplu, lodash) pentru debouncing
  • folosind React Hooks API

Sper că vă ajută 🙂

import React, { useState, useEffect, ChangeEvent } from 'react';

export default function DebouncedSearchBox({
  inputType,
  handleSearch,
  placeholder,
  debounceInterval,
}: {
  inputType?: string;
  handleSearch: (q: string) => void;
  placeholder: string;
  debounceInterval: number;
}) {
  const [query, setQuery] = useState<string>('');
  const [timer, setTimer] = useState<NodeJS.Timer | undefined>();

  useEffect(() => {
    if (timer) {
      clearTimeout(timer);
    }
    setTimer(setTimeout(() => {
      handleSearch(query);
    }, debounceInterval));
  }, [query]);

  const handleOnChange = (e: ChangeEvent<HTMLInputElement>): void => {
    setQuery(e.target.value);
  };

  return (
    <input
      type={inputType || 'text'}
      className="form-control"
      placeholder={placeholder}
      value={query}
      onChange={handleOnChange}
    />
  );
}

Art

Există o use-debounce pachet pe care îl puteți utiliza cu ReactJS hooks.

Din README-ul pachetului:

import { useDebounce } from 'use-debounce';

export default function Input() {
  const [text, setText] = useState('Hello');
  const [value] = useDebounce(text, 1000);

  return (
    <div>
      <input
        defaultValue={'Hello'}
        onChange={(e) => {
          setText(e.target.value);
        }}
      />
      <p>Actual value: {text}</p>
      <p>Debounce value: {value}</p>
    </div>
  );
}

După cum puteți vedea din exemplul de mai sus, este configurat pentru a actualiza variabila value doar o dată la fiecare secundă (1000 de milisecunde).

Comentarii

  • Încă cea mai bună alegere în ianuarie 2021 –  > Por andruso.
  • deci dacă vreau să declanșez un eveniment de fiecare dată când valoarea este setată, o voi face așa ? – useEffect(() => { // function here }, [value]); –  > Por keemahs.
Rebs

Există acum o altă soluție pentru React și React Native în sfârșitul anului 2019:

react-debounce-component

<input>
<Debounce ms={500}>
  <List/>
</Debounce>

Este o componentă, ușor de folosit, mică și cu suport larg

Exemplu:

import React from 'react';
import Debounce from 'react-debounce-component';

class App extends React.Component {
  constructor (props) {
    super(props);
    this.state = {value: 'Hello'}
  }
  render () {
    return (
      <div>
        <input value={this.state.value} onChange={(event) => {this.setState({value: event.target.value})}}/>
        <Debounce ms={1000}>
          <div>{this.state.value}</div>
        </Debounce>
      </div>
    );
  }
}

export default App;

*Eu sunt creatorul acestei componente

Adam Pietrasiak

Soluția mea este bazată pe cârlige (scrisă în Typescript).

Am 2 cârlige principale useDebouncedValue și useDebouncedCallback

Primul – useDebouncedValue

Să spunem că avem o casetă de căutare, dar vrem să cerem serverului rezultatele căutării după ce utilizatorul nu mai tastează timp de 0,5s

function SearchInput() {
  const [realTimeValue, setRealTimeValue] = useState('');

  const debouncedValue = useDebouncedValue(realTimeValue, 500); // this value will pick real time value, but will change it's result only when it's seattled for 500ms

  useEffect(() => {
    // this effect will be called on seattled values
    api.fetchSearchResults(debouncedValue);
  }, [debouncedValue])

  return <input onChange={event => setRealTimeValue(event.target.value)} />
}

Implementare

import { useState, useEffect } from "react";

export function useDebouncedValue<T>(input: T, time = 500) {
  const [debouncedValue, setDebouncedValue] = useState(input);

  // every time input value has changed - set interval before it's actually commited
  useEffect(() => {
    const timeout = setTimeout(() => {
      setDebouncedValue(input);
    }, time);

    return () => {
      clearTimeout(timeout);
    };
  }, [input, time]);

  return debouncedValue;
}

Al doilea useDebouncedCallback

Pur și simplu se creează o funcție „debounced” în domeniul de aplicare al componentei dvs.

Să spunem că avem o componentă cu un buton care va afișa o alertă la 500ms după ce nu mai faceți clic pe el.

function AlertButton() {
  function showAlert() {
    alert('Clicking has seattled');
  }

  const debouncedShowAlert = useDebouncedCallback(showAlert, 500);

  return <button onClick={debouncedShowAlert}>Click</button>
}

Implementare (rețineți că folosesc lodash/debounce ca ajutor)

import debounce from 'lodash/debounce';
import { useMemo } from 'react';

export function useDebouncedCallback<T extends (...args: any) => any>(callback: T, wait?: number) {
  const debouncedCallback = useMemo(() => debounce(callback, wait), [callback, wait]);

  return debouncedCallback;
}

Bruno Silvano

O soluție frumoasă și curată, care nu necesită dependențe externe:

Debouncing cu React Hooks

Folosește un customizat plus cârligele React useEffect și setTimeout / clearTimeout metodă.

puchu

Doar o altă variantă cu react și lodash recente.

class Filter extends Component {
  static propTypes = {
    text: PropTypes.string.isRequired,
    onChange: PropTypes.func.isRequired
  }

  state = {
    initialText: '',
    text: ''
  }

  constructor (props) {
    super(props)

    this.setText = this.setText.bind(this)
    this.onChange = _.fp.debounce(500)(this.onChange.bind(this))
  }

  static getDerivedStateFromProps (nextProps, prevState) {
    const { text } = nextProps

    if (text !== prevState.initialText) {
      return { initialText: text, text }
    }

    return null
  }

  setText (text) {
    this.setState({ text })
    this.onChange(text)
  }

  onChange (text) {
    this.props.onChange(text)
  }

  render () {
    return (<input value={this.state.text} onChange={(event) => this.setText(event.target.value)} />)
  }
}

Jivko Jelev

Ați încercat?

function debounce(fn, delay) {
  var timer = null;
  return function() {
    var context = this,
      args = arguments;
    clearTimeout(timer);
    timer = setTimeout(function() {
      fn.apply(context, args);
    }, delay);
  };
}

var SearchBox = React.createClass({
  render: function() {
    return <input type="search" name="p" onChange={this.handleOnChange} />;
  },

  handleOnChange: function(event) {
    debounce(\ Your handleChange code , 200);
  }
});

user1079877

Această soluție nu are nevoie de nicio librărie suplimentară și, de asemenea, pornește lucrurile atunci când utilizatorul apasă enter:

const debounce = (fn, delay) => {
    let timer = null;
    return function() {
        const context = this,
        args = arguments;
        clearTimeout(timer);
        timer = setTimeout(() => {
            fn.apply(context, args);
        }, delay);
    };  
}

const [search, setSearch] = useState('');
const [searchFor, setSearchFor] = useState(search);

useEffect(() => {
    console.log("Search:", searchFor);
}, [searchFor]);

const fireChange = event => {
    const { keyCode } = event;
    if (keyCode === 13) {
        event.preventDefault();
        setSearchFor(search);
    }
}

const changeSearch = event => {
    const { value } = event.target;
    setSearch(value);
    debounceSetSearchFor(value);
};

const debounceSetSearchFor = useCallback(debounce(function(value) {
    setSearchFor(value);
}, 250), []);

iar intrarea ar putea fi de genul:

<input value={search} onKeyDown={fireChange} onChange={changeSearch}  />

Comentarii

  • Pureeeeeeeeeeeee JS, Îmi place –  > Por a_m_dev.
Xinan

Nu pot găsi niciun răspuns la această întrebare care să menționeze abordarea pe care o folosesc, așa că vreau doar să ofer o soluție alternativă aici, care cred că este cea mai bună pentru cazul meu de utilizare.

Dacă utilizați librăria populară a setului de instrumente react hooks numită react-use, atunci există un cârlig de utilitate numit useDebounce() care a implementat logica de denunțare într-un mod destul de elegant.

const [query, setQuery] = useState('');

useDebounce(
  () => {
    emitYourOnDebouncedSearchEvent(query);
  },
  2000,
  [query]
);

return <input onChange={({ currentTarget }) => setQuery(currentTarget.value)} />

Pentru detalii, vă rugăm să verificați direct pagina github a lib.

https://github.com/streamich/react-use/blob/master/docs/useDebounce.md

Robert

În loc să înfășurați handleOnChange într-un debounce(), de ce să nu înfășurați apelul ajax în interiorul funcției de callback din interiorul debounce, astfel nu distrugeți obiectul evenimentului. Deci, ceva de genul acesta:

handleOnChange: function (event) {
   debounce(
     $.ajax({})
  , 250);
}

Comentarii

  • Deoarece obiectul de eveniment nu este imuabil și este distrus de ReactJS, deci chiar dacă înfășurați și obțineți o captură de închidere, codul va eșua. –  > Por Henrik.
mlucool

Iată un exemplu pe care l-am găsit și care înfășoară o altă clasă cu un debouncer. Aceasta se pretează foarte bine să fie transformată într-un decorator/funcție de ordin superior:

export class DebouncedThingy extends React.Component {
    static ToDebounce = ['someProp', 'someProp2'];
    constructor(props) {
        super(props);
        this.state = {};
    }
    // On prop maybe changed
    componentWillReceiveProps = (nextProps) => {
        this.debouncedSetState();
    };
    // Before initial render
    componentWillMount = () => {
        // Set state then debounce it from here on out (consider using _.throttle)
        this.debouncedSetState();
        this.debouncedSetState = _.debounce(this.debouncedSetState, 300);
    };
    debouncedSetState = () => {
        this.setState(_.pick(this.props, DebouncedThingy.ToDebounce));
    };
    render() {
        const restOfProps = _.omit(this.props, DebouncedThingy.ToDebounce);
        return <Thingy {...restOfProps} {...this.state} />
    }
}

Thread Pitt

Iată un fragment care folosește abordarea lui @Abra înfășurată într-o componentă funcțională (folosim fabric pentru UI, doar înlocuiți-o cu un simplu buton)

import React, { useCallback } from "react";
import { debounce } from "lodash";

import { PrimaryButton, DefaultButton } from 'office-ui-fabric-react/lib/Button';

const debounceTimeInMS = 2000;

export const PrimaryButtonDebounced = (props) => {

    const debouncedOnClick = debounce(props.onClick, debounceTimeInMS, { leading: true });

    const clickHandlerDebounced = useCallback((e, value) => {

        debouncedOnClick(e, value);

    },[]);

    const onClick = (e, value) => {

        clickHandlerDebounced(e, value);
    };

    return (
        <PrimaryButton {...props}
            onClick={onClick}
        />
    );
}

Edward

Căutam o soluție la aceeași problemă și am dat peste acest fir, precum și peste altele, dar au avut aceeași problemă: dacă încercați să faceți o handleOnChange funcție și aveți nevoie de valoarea de la o țintă de eveniment, veți obține cannot read property value of null sau o eroare asemănătoare. În cazul meu, aveam nevoie, de asemenea, să păstrez contextul lui this în interiorul funcției deblocate, deoarece execut o acțiune fluxibilă. Iată soluția mea, funcționează bine pentru cazul meu de utilizare, așa că o las aici în cazul în care cineva întâlnește acest fir de discuție:

// at top of file:
var myAction = require('../actions/someAction');

// inside React.createClass({...});

handleOnChange: function (event) {
    var value = event.target.value;
    var doAction = _.curry(this.context.executeAction, 2);

    // only one parameter gets passed into the curried function,
    // so the function passed as the first parameter to _.curry()
    // will not be executed until the second parameter is passed
    // which happens in the next function that is wrapped in _.debounce()
    debouncedOnChange(doAction(myAction), value);
},

debouncedOnChange: _.debounce(function(action, value) {
    action(value);
}, 300)

Fareed Alnamrouti

pentru throttle sau debounce cel mai bun mod este să creați un creator de funcții, astfel încât să îl puteți utiliza oriunde, de exemplu:

  updateUserProfileField(fieldName) {
    const handler = throttle(value => {
      console.log(fieldName, value);
    }, 400);
    return evt => handler(evt.target.value.trim());
  }

și în render metoda puteți face:

<input onChange={this.updateUserProfileField("givenName").bind(this)}/>

updateUserProfileField metoda va crea o funcție separată de fiecare dată când o apelați.

Notă: nu încercați să returnați direct gestionarul, de exemplu, acest lucru nu va funcționa:

 updateUserProfileField(fieldName) {
    return evt => throttle(value => {
      console.log(fieldName, value);
    }, 400)(evt.target.value.trim());
  }

motivul pentru care acest lucru nu va funcționa este că va genera o nouă funcție de accelerare de fiecare dată când evenimentul este apelat în loc să folosească aceeași funcție de accelerare, deci, practic, acceleratorul va fi inutil 😉

De asemenea, dacă utilizați debounce sau throttle nu aveți nevoie de setTimeout sau clearTimeout, acesta este de fapt motivul pentru care le folosim 😛

Francisco Hanna

Am întâlnit această problemă astăzi. Am rezolvat-o folosind setTimeout și clearTimeout.

Voi da un exemplu pe care l-ați putea adapta:

import React, { Component } from 'react'

const DEBOUNCE_TIME = 500

class PlacesAutocomplete extends Component {
  debounceTimer = null;

  onChangeHandler = (event) => {
    // Clear the last registered timer for the function
    clearTimeout(this.debounceTimer);

    // Set a new timer
    this.debounceTimer = setTimeout(
      // Bind the callback function to pass the current input value as arg
      this.getSuggestions.bind(null, event.target.value), 
      DEBOUNCE_TIME
    )
  }

  // The function that is being debounced
  getSuggestions = (searchTerm) => {
    console.log(searchTerm)
  }

  render() {
    return (
      <input type="text" onChange={this.onChangeHandler} />
    )
  }
}

export default PlacesAutocomplete

De asemenea, ați putea să o refactorizați în propria componentă de funcție:

import React from 'react'

function DebouncedInput({ debounceTime, callback}) {
  let debounceTimer = null
  return (
    <input type="text" onChange={(event) => {
      clearTimeout(debounceTimer);

      debounceTimer = setTimeout(
        callback.bind(null, event.target.value), 
        debounceTime
      )
    }} />
  )
}

export default DebouncedInput

și să o folosiți ca:

import React, { Component } from 'react'
import DebouncedInput from '../DebouncedInput';

class PlacesAutocomplete extends Component {
  debounceTimer = null;

  getSuggestions = (searchTerm) => {
    console.log(searchTerm)
  }

  render() {
    return (
      <DebouncedInput debounceTime={500} callback={this.getSuggestions} />
    )
  }
}

export default PlacesAutocomplete

Vince

Dacă aveți nevoie doar să efectuați un debounce într-un buton pentru solicitarea de date, codul furnizat v-ar putea fi de ajutor:

  1. Creați o funcție pentru a preveni implicit cu declarație condiționată if requesting is true sau false

  2. Implementați funcția useState Hook și useEffect Hook

const PageOne = () => {
 const [requesting, setRequesting] = useState(false);

  useEffect(() => {
    return () => {
      setRequesting(false);
    };
  }, [requesting]);

  const onDebounce = (e) => {
    if (requesting === true) {
      e.preventDefault();
    }
    // ACTIONS
    setLoading(true);
  };

 return (
  <div>
    
    <button onClick={onDebounce}>Requesting data</button>
  </div>
 )
}

Gerson Diniz

Cârlig:

import {useState} from "react";

const useDebounce = ({defaultTimeout = 250, defaultIdentifier = 'default'} = {}) => {

    const [identifiers, setIdentifiers] = useState({[defaultIdentifier]: null});

    return ({fn = null, identifier = defaultIdentifier, timeout = defaultTimeout} = {}) => {
        if (identifiers.hasOwnProperty(identifier)) clearTimeout(identifiers[identifier]);
        setIdentifiers({...identifiers, [identifier]: setTimeout(fn, timeout)});
    };
};

export default useDebounce;

Și use oriunde (în același fișier folosiți identificator pentru a preveni concurența) de exemplu:

const debounce = useDebounce();

const handlerA = () => {
    debounce({fn: () => console.log('after 2000ms of last call with identifier A'), identifier: 'A', timeout: 2000});
};

const handlerB = () => {
    debounce({fn: () => console.log('after 1500ms of last call with identifier B'), identifier: 'B', timeout: 1500});
};

Comentarii

  • arată bine, dacă înțeleg bine, un caz de utilizare poate arăta ca următorul fragment: const debounce = useDebounce(); const debouncedSearchInputHandler = (event) => { setSearchInput(event.target.value); debounce({fn: () => startRestCall(event.target.value), timeout: 1000}); }; –  > Por Markus Schulz.
Andrei

Iată un exemplu TypeScript funcțional pentru cei care folosesc TS și doresc să debounce async funcții.

function debounce<T extends (...args: any[]) => any>(time: number, func: T): (...funcArgs: Parameters<T>) => Promise<ReturnType<T>> {
     let timeout: Timeout;

     return (...args: Parameters<T>): Promise<ReturnType<T>> => new Promise((resolve) => {
         clearTimeout(timeout);
         timeout = setTimeout(() => {
             resolve(func(...args));
         }, time)
     });
 }

anaval

un pic cam târziu aici, dar asta ar trebui să vă ajute. creați această clasă (este scrisă în typescript, dar este ușor de convertit în javascript)

export class debouncedMethod<T>{
  constructor(method:T, debounceTime:number){
    this._method = method;
    this._debounceTime = debounceTime;
  }
  private _method:T;
  private _timeout:number;
  private _debounceTime:number;
  public invoke:T = ((...args:any[])=>{
    this._timeout && window.clearTimeout(this._timeout);
    this._timeout = window.setTimeout(()=>{
      (this._method as any)(...args);
    },this._debounceTime);
  }) as any;
}

și pentru a utiliza

var foo = new debouncedMethod((name,age)=>{
 console.log(name,age);
},500);
foo.invoke("john",31);

Behnam Mohammadi

puteți folosi tlence tlence

function log(server) {
  console.log('connecting to', server);
}

const debounceLog = debounce(log, 5000);
// just run last call to 5s
debounceLog('local');
debounceLog('local');
debounceLog('local');
debounceLog('local');
debounceLog('local');
debounceLog('local');

srcspider

Soluția lui Julen este cam greu de citit, iată un cod react mai clar și la obiect pentru oricine care l-a împiedicat pe baza titlului și nu a detaliilor mici ale întrebării.

versiunea tl;dr: atunci când ați vrea să faceți update la observatori trimiteți în schimb un apel la o metodă de planificare și care la rândul ei va notifica de fapt observatorii (sau va efectua ajax, etc)

JFiddle complet cu o componentă de exemplu jsfiddle

var InputField = React.createClass({

    getDefaultProps: function () {
        return {
            initialValue: '',
            onChange: null
        };
    },

    getInitialState: function () {
        return {
            value: this.props.initialValue
        };
    },

    render: function () {
        var state = this.state;
        return (
            <input type="text"
                   value={state.value}
                   onChange={this.onVolatileChange} />
        );
    },

    onVolatileChange: function (event) {
        this.setState({ 
            value: event.target.value 
        });

        this.scheduleChange();
    },

    scheduleChange: _.debounce(function () {
        this.onChange();
    }, 250),

    onChange: function () {
        var props = this.props;
        if (props.onChange != null) {
            props.onChange.call(this, this.state.value)
        }
    },

});

Comentarii

  • Acest lucru nu va face ca starea/timpul debounce-ului să fie global pentru toate instanțele InputField, deoarece este creat odată cu definiția clasei? Poate că asta este ceea ce doriți, dar merită menționat oricum. –  > Por robbles.
  • periculos dacă este montat de mai multe ori în dom, verificați stackoverflow.com/questions/23123138/… – –  > Por Sebastien Lorber.
  • Aceasta este o soluție proastă, din cauza problemelor legate de montarea dublă – faceți din funcția dvs. pentru a programaChange un singleton și asta nu este o idee bună. -1 –  > Por Henrik.
Dominic

Evitați să utilizați event.persist() – dacă doriți să lăsați React să recicleze evenimentul sintetic. Cred că cel mai curat mod, indiferent dacă folosiți clase sau cârlige, este să împărțiți callback-ul în două bucăți:

  1. Callback-ul fără debouncing
  2. apelează o funcție debounced cu doar bucățile de eveniment de care aveți nevoie (astfel încât evenimentul sintetic să poată fi reciclat)

Clase

handleMouseOver = throttle(target => {
  console.log(target);
}, 1000);

onMouseOver = e => {
  this.handleMouseOver(e.target);
};

<div onMouseOver={this.onMouseOver} />

Funcții

const handleMouseOver = useRef(throttle(target => {
  console.log(target);
}, 1000));

function onMouseOver(e) {
  handleMouseOver.current(e.target);
}

<div onMouseOver={this.onMouseOver} />

Rețineți că, în cazul în care handleMouseOver utilizează starea din cadrul componentei, trebuie să utilizați useMemo în loc de useRef și treceți-le ca dependențe, altfel veți lucra cu date vechi (nu se aplică, desigur, claselor).

Micah B.

Extindeți cârligul useState

import { useState } from "react";
import _ from "underscore"
export const useDebouncedState = (initialState, durationInMs = 500) => {
    const [internalState, setInternalState] = useState(initialState);
    const debouncedFunction = _.debounce(setInternalState, durationInMs);
    return [internalState, debouncedFunction];
};
export default useDebouncedState;

Cârlig de utilizare

import useDebouncedState from "../hooks/useDebouncedState"
//...
const [usernameFilter, setUsernameFilter] = useDebouncedState("")
//...
<input id="username" type="text" onChange={e => setUsernameFilter(e.target.value)}></input>

https://trippingoncode.com/react-debounce-hook/

tomatentobi

Dacă nu vă place să adăugați lodash sau orice alt pachet:

import React, { useState, useRef } from "react";

function DebouncedInput() {
  const [isRefetching, setIsRefetching] = useState(false);
  const [searchTerm, setSearchTerm] = useState("");
  const previousSearchTermRef = useRef("");

  function setDebouncedSearchTerm(value) {
    setIsRefetching(true);
    setSearchTerm(value);
    previousSearchTermRef.current = value;
    setTimeout(async () => {
      if (previousSearchTermRef.current === value) {
        try {
          // await refetch();
        } finally {
          setIsRefetching(false);
        }
      }
    }, 500);
  }

  return (
    <input
      value={searchTerm}
      onChange={(event) => setDebouncedSearchTerm(event.target.value)}
    />
  );
}

Ievgen Naida

Soluție de exemplu de debounce și anulare React ajax folosind React Hooks și programare reactivă (RxJS):

import React, { useEffect, useState } from "react";
import { ajax } from "rxjs/ajax";
import { debounceTime, delay, takeUntil } from "rxjs/operators";
import { Subject } from "rxjs/internal/Subject";

const App = () => {
  const [items, setItems] = useState([]);
  const [loading, setLoading] = useState(true);
  const [filterChangedSubject] = useState(() => {
    // Arrow function is used to init Singleton Subject. (in a scope of a current component)
    return new Subject<string>();
  });

  useEffect(() => {
    // Effect that will be initialized once on a react component init.
    const subscription = filterChangedSubject
      .pipe(debounceTime(200))
      .subscribe((filter) => {
        if (!filter) {
          setLoading(false);
          setItems([]);
          return;
        }
        ajax(`https://swapi.dev/api/people?search=${filter}`)
          .pipe(
            // current running ajax is canceled on filter change.
            takeUntil(filterChangedSubject)
          )
          .subscribe(
            (results) => {
              // Set items will cause render:
              setItems(results.response.results);
            },
            () => {
              setLoading(false);
            },
            () => {
              setLoading(false);
            }
          );
      });

    return () => {
      // On Component destroy. notify takeUntil to unsubscribe from current running ajax request
      filterChangedSubject.next("");
      // unsubscribe filter change listener
      subscription.unsubscribe();
    };
  }, []);

  const onFilterChange = (e) => {
    // Notify subject about the filter change
    filterChangedSubject.next(e.target.value);
  };
  return (
    <div>
      Cards
      {loading && <div>Loading...</div>}
      <input onChange={onFilterChange}></input>
      {items && items.map((item, index) => <div key={index}>{item.name}</div>)}
    </div>
  );
};

export default App;

Chandrakesha Rao
class UserListComponent extends Component {
    constructor(props) {
        super(props);
        this.searchHandler = this.keyUpHandler.bind(this);
        this.getData = this.getData.bind(this);
        this.magicSearch = this.magicSearch.bind(this,500);
    }
    getData = (event) => {
        console.log(event.target.value);
    }
    magicSearch = function (fn, d) {
        let timer;
        return function () {
            let context = this;
            clearTimeout(timer);
            timer = setTimeout(() => {
                fn.apply(context, arguments)
            }, d);
        }
    }
    keyUpHandler = this.magicSearch(this.getData, 500);
    render() {
        return (
             <input type="text" placeholder="Search" onKeyUp={this.searchHandler} />
        )
    }
}

canvaskisa

De asemenea, puteți utiliza un mixin scris de dvs. însuși, ceva de genul acesta:

var DebounceMixin = {
  debounce: function(func, time, immediate) {
    var timeout = this.debouncedTimeout;
    if (!timeout) {
      if (immediate) func();
      this.debouncedTimeout = setTimeout(function() {
        if (!immediate) func();
        this.debouncedTimeout = void 0;
      }.bind(this), time);
    }
  }
};

Și apoi folosiți-l în componenta dvs. astfel:

var MyComponent = React.createClass({
  mixins: [DebounceMixin],
  handleClick: function(e) {
    this.debounce(function() {
      this.setState({
        buttonClicked: true
      });
    }.bind(this), 500, true);
  },
  render: function() {
    return (
      <button onClick={this.handleClick}></button>
    );
  }
});

Comentarii

  • Asta nu este debounce, ci „delay”. Debounce resetează timeout-ul la fiecare eveniment care se întâmplă înainte de timeout. -1 –  > Por Henrik.
  • @Henrik Greșeala mea, aveți dreptate. Apropo, este ușor să faci debounce așa. –  > Por canvaskisa.
  • această soluție nu răspunde la întrebare, deoarece ar declanșa acțiunea exact după expirarea timpului specificat. Dar, în acest subiect, timeout-ul ar trebui să fie „extensibil” dacă debounce este apelat de mai multe ori în intervalul de timp. –  > Por Breaker222.