Как вы выполняете debounce в React.js?
Я хочу отменить handleOnChange.
Я пробовал с debounce(this.handleOnChange, 200)
, но он не работает.
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
}
});
Мы часто хотим отменить вызовы API, чтобы избежать затопления серверной части бесполезными запросами.
В 2018 году работа с обратными вызовами (Lodash/Underscore) кажется мне плохой и подверженной ошибкам. Проблемы с параллельным доступом и параллелизмом легко возникают из-за разрешения вызовов API в произвольном порядке.
Я создал небольшую библиотеку для React, чтобы решить ваши проблемы: awesome-debounce-обещание.
Это не должно быть сложнее, чем это:
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 });
};
}
Деблокированная функция гарантирует, что:
this.setState({ result });
произойдет за вызов APIВ конце концов, вы можете добавить еще один трюк, если ваш компонент размонтируется:
componentWillUnmount() {
this.setState = () => {};
}
Обратите внимание, что Observables (RxJS) также могут отлично подходить для разбора входных данных, но это более мощная абстракция, которая может быть сложнее для правильного изучения/использования.
Важной частью здесь является создание единой дебазированной (или регулируемой) функции для экземпляра компонента. Вы не хотите пересоздавать функцию debounce (или throttle) каждый раз, и вы не хотите, чтобы несколько экземпляров совместно использовали одну и ту же функцию debounce.
В этом ответе я не определяю функцию устранения неполадок, так как она не очень актуальна, но этот ответ будет отлично работать с _.debounce
подчеркивания или lodash, а также с любой предоставленной пользователем функцией устранения неполадок.
Поскольку отклоняемые функции сохраняют состояние, мы должны создать одну отклоненную функцию для каждого экземпляра компонента.
ES6 (свойство класса): рекомендуется
class SearchBox extends React.Component {
method = debounce(() => {
...
});
}
ES6 (конструктор класса)
class SearchBox extends React.Component {
constructor(props) {
super(props);
this.method = debounce(this.method,1000);
}
method() { ... }
}
ES5
var SearchBox = React.createClass({
method: function() {...},
componentWillMount: function() {
this.method = debounce(this.method,100);
},
});
См. JsFiddle: 3 экземпляра создают 1 запись в журнале за экземпляр (что составляет 3 глобально).
var SearchBox = React.createClass({
method: function() {...},
debouncedMethod: debounce(this.method, 100);
});
Это не будет работать, потому что при создании объекта описания класса this
не сам объект, созданный. this.method
не возвращает того, что вы ожидаете, потому что this
контекст не является самим объектом (который на самом деле еще не существует, кстати, поскольку он только создается).
var SearchBox = React.createClass({
method: function() {...},
debouncedMethod: function() {
var debounced = debounce(this.method,100);
debounced();
},
});
На этот раз вы эффективно создаете дебагованную функцию, которая вызывает ваш this.method
. Проблема в том, что вы воссоздаете его при каждом вызове debouncedMethod
, поэтому вновь созданная функция debounce ничего не знает о предыдущих вызовах! Вы должны повторно использовать одну и ту же отклоненную функцию с течением времени, иначе удаление не произойдет.
var SearchBox = React.createClass({
debouncedMethod: debounce(function () {...},100),
});
Это немного сложно здесь.
Все подключенные экземпляры класса будут использовать одну и ту же функцию, от которой отказались, и чаще всего это не то, что вам нужно! См. JsFiddle: 3 экземпляра производят только 1 запись журнала во всем мире.
Для каждого экземпляра компонента необходимо создать неработающую функцию, а не отдельную деблокированную функцию на уровне класса, которая будет использоваться каждым экземпляром компонента.
Это связано с тем, что мы часто хотим отсеивать или ограничивать события DOM.
В React объекты событий (т. SyntheticEvent
), которые вы получаете в обратных вызовах, объединяются (теперь это задокументировано). Это означает, что после вызова обратного вызова события полученное SyntheticEvent будет помещено обратно в пул с пустыми атрибутами, чтобы уменьшить нагрузку на GC.
Таким образом, если вы SyntheticEvent
свойствам SyntheticEvent
асинхронно с исходным обратным вызовом (как это может быть в случае, если вы дросселируете/отклоняете), свойства, к которым вы обращаетесь, могут быть удалены. Если вы хотите, чтобы событие никогда не возвращалось в пул, вы можете использовать метод persist()
.
onClick = e => {
alert('sync -> hasNativeEvent=${!!e.nativeEvent}');
setTimeout(() => {
alert('async -> hasNativeEvent=${!!e.nativeEvent}');
}, 0);
};
2-й (асинхронный) напечатает hasNativeEvent=false
потому что свойства события были очищены.
onClick = e => {
e.persist();
alert('sync -> hasNativeEvent=${!!e.nativeEvent}');
setTimeout(() => {
alert('async -> hasNativeEvent=${!!e.nativeEvent}');
}, 0);
};
Второй (асинхронный) напечатает hasNativeEvent=true
, потому что persist
позволяет избежать помещать событие обратно в бассейне.
Вы можете проверить эти 2 поведения здесь: JsFiddle
Прочитайте ответ Джулена для примера использования persist()
с функцией throttle/debounce.
handleOnChange = debounce((e) => { /* onChange handler code here */ }, timeout)
на верхнем уровне вашего класса. Вы по-прежнему эффективно устанавливаете элемент экземпляра, но это выглядит немного больше как обычное определение метода. Нет необходимости в constructor
если у вас его еще нет. Я полагаю, это в основном стиль предпочтений.
Вы можете использовать метод event.persist()
.
В следующем примере следует использовать знак подчеркивания _.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} />
);
}
});
Изменить: см. этот JSFiddle
Обновление: приведенный выше пример показывает неконтролируемый компонент. Я использую управляемые элементы все время, так что вот еще один пример выше, но без использования event.persist()
"обмана".
A Также доступен JSFiddle. Пример без подчеркивания
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);
Изменить: обновленные примеры и JSFiddles для изменения 0.12
Изменить: обновленные примеры для решения проблемы, поднятой Себастьяном Лорбером
Изменить: обновлено с помощью jsfiddle, который не использует подчеркивание и использует простой javascript-debounce.
Если все, что вам нужно от объекта события, - это получить элемент ввода DOM, решение будет намного проще - просто используйте ref
. Обратите внимание, что это требует подчеркивания:
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} />
}
}
Я нашел этот пост Джастином Тулком очень полезным. После нескольких попыток, что, как можно было бы воспринимать как более официальный способ с реакцией/редукцией, показывает, что это не удается из-за искусственного объединения событий React. Затем его решение использует некоторое внутреннее состояние для отслеживания значения, измененного/введенного во входных данных, с обратным вызовом сразу после setState
который вызывает дросселированное /setState
действие, которое показывает некоторые результаты в реальном времени.
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}
/>
)
}
}
Использование ES6 CLASS и React 15.xx & lodash.debounce Im использования Реагировать реф здесь, так как потери Ивентов это связывают внутренне.
class UserInput extends React.Component {
constructor(props) {
super(props);
this.state = {
userInput: ""
};
this.updateInput = _.debounce(this.updateInput, 500);
}
updateInput(userInput) {
this.setState({
userInput
});
//OrderActions.updateValue(userInput);//do some server stuff
}
render() {
return ( <div>
<p> User typed: {
this.state.userInput
} </p>
<input ref = "userValue" onChange = {() => this.updateInput(this.refs.userValue.value) } type = "text" / >
</div>
);
}
}
ReactDOM.render( <
UserInput / > ,
document.getElementById('root')
);
<script src="https://cdn.jsdelivr.net/npm/[email protected]/lodash.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="root"></div>
Здесь много полезной информации, но, чтобы быть кратким. Это работает для меня...
import React, {Component} from 'react';
import _ from 'lodash';
class MyComponent extends Component{
constructor(props){
super(props);
this.handleChange = _.debounce(this.handleChange.bind(this),700);
};
Поработав некоторое время с вводом текста и не найдя идеального решения самостоятельно, я нашел это на npm https://www.npmjs.com/package/react-debounce-input
Вот простой пример:
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);
Компонент DebounceInput принимает все реквизиты, которые вы можете назначить обычному элементу ввода. Попробуйте это на codepen
Я надеюсь, что это поможет кому-то еще и сэкономит им время.
Вы можете использовать метод Lodash debounce https://lodash.com/docs/4.17.5#debounce. Это просто и эффективно.
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);
}
Вы также можете отменить метод debounce, используя метод ниже.
this.debounceHandleUpdate.cancel();
Надеюсь, это поможет вам. Ура !!
Если вы используете redux, вы можете сделать это очень элегантно с помощью промежуточного программного обеспечения. Вы можете определить промежуточное ПО Debounce
как:
var timeout;
export default store => next => action => {
const { meta = {} } = action;
if(meta.debounce){
clearTimeout(timeout);
timeout = setTimeout(() => {
next(action)
}, meta.debounce)
}else{
next(action)
}
}
Затем вы можете добавить создателей debouncing в action, например:
export default debouncedAction = (payload) => ({
type : 'DEBOUNCED_ACTION',
payload : payload,
meta : {debounce : 300}
}
На самом деле уже промежуточное программное обеспечение вы можете выйти из npm, чтобы сделать это за вас.
applyMiddleware(...)
если у нас много
Вот пример, который я придумал, который обертывает другой класс с помощью debouncer. Это хорошо пригодится для превращения в функцию декоратора/более высокого порядка:
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} />
}
}
для throttle
или debounce
лучший способ - создать создатель функции, чтобы вы могли использовать его где угодно, например:
updateUserProfileField(fieldName) {
const handler = throttle(value => {
console.log(fieldName, value);
}, 400);
return evt => handler(evt.target.value.trim());
}
и в вашем методе render
вы можете:
<input onChange={this.updateUserProfileField("givenName").bind(this)}/>
метод updateUserProfileField
будет создавать отдельную функцию каждый раз, когда вы ее вызываете.
Примечание не пытайтесь вернуть обработчик напрямую, например, это не сработает:
updateUserProfileField(fieldName) {
return evt => throttle(value => {
console.log(fieldName, value);
}, 400)(evt.target.value.trim());
}
причина, по которой это не сработает, потому что это будет генерировать новую функцию дроссельной заслонки каждый раз, когда вызванное событие вместо использования одной и той же функции дроссельной заслонки, так что дроссель будет бесполезен;)
Также, если вы используете debounce
или throttle
, вам не нужны setTimeout
или clearTimeout
, на самом деле мы их используем: P
Я искал решение одной и той же проблемы и наткнулся на этот поток, а также на некоторые другие, но у них была одна и та же проблема: если вы пытаетесь выполнить функцию handleOnChange
, и вам нужно значение из целевого объекта, вы получите cannot read property value of null
или некоторую такую ошибку. В моем случае мне также необходимо сохранить контекст this
внутри дебявленной функции, так как я выполняю потоковое действие. Здесь мое решение, оно хорошо работает для моего прецедента, поэтому я оставляю его здесь, если кто-нибудь встретит этот поток:
// 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)
Вместо того, чтобы обертывать handleOnChange в debounce(), почему бы не обернуть вызов ajax внутри функции обратного вызова внутри debounce, тем самым не разрушив объект события. Так что-то вроде этого:
handleOnChange: function (event) {
debounce(
$.ajax({})
, 250);
}
вы можете использовать Tlence https://www.npmjs.com/package/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');
Просто еще один вариант с недавними реакциями и лодашами.
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)} />)
}
}
Решение Julen довольно сложно прочитать, здесь более ясный и точный код для всех, кто наткнулся на него, основываясь на заголовке, а не на крошечных деталях вопроса.
tl; dr version: когда вы обновляете наблюдателей, вместо этого отправляйте вызов метода расписания, а это, в свою очередь, будет фактически уведомлять наблюдателей (или выполнять ajax и т.д.)
Завершите jsfiddle с примером компонента http://jsfiddle.net/7L655p5L/4/
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)
}
},
});
Вы также можете использовать самоналоженный микшинг, что-то вроде этого:
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);
}
}
};
И затем используйте его в своем компоненте следующим образом:
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>
);
}
});
Не нужно тонны локальных переменных для приличной функции газа. Целью функции регулирования является сокращение ресурсов браузера, а не применение таких дополнительных затрат, которые вы используете еще больше. В качестве доказательства этого утверждения я разработал дроссельную функцию, которая имеет только 4 "зависающие" переменные. (Переменная "зависания" - это переменная, которая никогда не собирается сборщиком мусора, потому что на нее всегда ссылается функция, которая потенциально может быть вызвана, тем самым впитывая память.) Горстка дроссельных функций обычно не приносит никакого вреда; но, если есть тысячи дросселированных функций, то памяти становится мало, если вы используете действительно неэффективную функцию дросселя. Мое решение ниже.
var timenow=self.performance ? performance.now.bind(performance) : Date.now;
function throttle(func, alternateFunc, minInterval) {
var lastTimeWent = -minInterval, freshArguments=null;
function executeLater(){
func.apply(null, freshArguments);
freshArguments = null;
lastTimeWent = 0;
}
return function() {
var newTimeWent = timenow();
if ((newTimeWent-lastTimeWent) > minInterval) {
lastTimeWent = newTimeWent;
return func.apply(null, arguments);
} else {
if (freshArguments === null)
setTimeout(executeLater, minInterval-(newTimeWent-lastTimeWent));
freshArguments = arguments;
if (typeof alternateFunc === "function")
return alternateFunc.apply(null, arguments);
}
};
}
Затем, чтобы обернуть эту функцию регулирования вокруг EventTarget для таких вещей, как щелчки DOM, события окна, XMLHttpRequests onprogress, FileReader onprogress и т.д., Например, так:
var tfCache = []; // throttled functions cache
function listen(source, eventName, func, _opts){
var i = 0, Len = tfCache.length, cF = null, options = _opts || {};
a: {
for (; i < Len; i += 4)
if (tfCache[i] === func &&
tfCache[i+1] === (options.ALTERNATE||null) &&
tfCache[i+2] === (options.INTERVAL||200)
) break a;
cF = throttle(func, options.ALTERNATE||null, options.INTERVAL||200);
tfCache.push(func, options.ALTERNATE||null, options.INTERVAL||200, cF);
}
source.addEventListener(eventName, cF || tfCache[i+3], _opts);
return cF === null; // return whether it used the cache or not
};
function mute(source, eventName, func, _opts){
var options = _opts || {};
for (var i = 0, Len = tfCache.length; i < Len; i += 4)
if (tfCache[i] === func &&
tfCache[i+1] === (options.ALTERNATE||null) &&
tfCache[i+2] === (options.INTERVAL||200)
) {
source.removeEventListener(eventName, tfCache[i+3], options);
return true;
}
return false;
}
Ниже приведен пример нажатия кнопки для прослушивания щелчка только один раз в секунду. Когда он получает этот щелчок, он меняется на новый случайный цвет.
(function(){"use strict";
// The function throttler //
var timenow=self.performance ? performance.now.bind(performance) : Date.now;
function throttle(func, alternateFunc, minInterval) {
var lastTimeWent = -minInterval, freshArguments=null;
function executeLater(){
func.apply(null, freshArguments);
freshArguments = null;
lastTimeWent = 0;
}
return function() {
var newTimeWent = timenow();
if ((newTimeWent-lastTimeWent) > minInterval) {
lastTimeWent = newTimeWent;
return func.apply(null, arguments);
} else {
if (freshArguments === null)
setTimeout(executeLater,minInterval-(newTimeWent-lastTimeWent));
freshArguments = arguments;
if (typeof alternateFunc === "function")
return alternateFunc.apply(this, arguments);
}
};
}
// The EventTarget wrapper: //
var tfCache = []; // throttled functions cache
function listen(source, eventName, func, _opts){
var i = 0, Len = tfCache.length, cF = null, options = _opts || {};
a: {
for (; i < Len; i += 4)
if (tfCache[i] === func &&
tfCache[i+1] === (options.ALTERNATE||null) &&
tfCache[i+2] === (options.INTERVAL||200)
) break a;
cF = throttle(func, options.ALTERNATE||null, options.INTERVAL||200);
tfCache.push(func, options.ALTERNATE||null, options.INTERVAL||200, cF);
}
source.addEventListener(eventName, cF || tfCache[i+3], _opts);
return cF === null; // return whether it used the cache or not
};
function mute(source, eventName, func, _opts){
var options = _opts || {};
for (var i = 0, Len = tfCache.length; i < Len; i += 4)
if (tfCache[i] === func &&
tfCache[i+1] === (options.ALTERNATE||null) &&
tfCache[i+2] === (options.INTERVAL||200)
) {
source.removeEventListener(eventName, tfCache[i+3], options);
return true;
}
return false;
}
// Finally, the color changing button: //
function randHex(){ // weighted towards the ends of the scales for contrast
var rand = Math.random()*2 - 1; // equally offcenter it from one
var sign = rand < 0 ? -1 : 1; // get a sign-ish value
rand = Math.sqrt(rand * sign) * sign; // stretch it further from zero
rand = 128 + rand * 128; // next, recenter it to range from 0 to 255
var str = (rand | 0).toString(16); // make an integer string
while (str.length < 2) str = "0" + str; // pad it
return str; // finally, return it
}
var clickerEle = document.getElementById("clicker");
var dropperEle = document.getElementById("droppedClicks");
var deDs = dropperEle.dataset; // deDs = droperEle DataSet
var dropSkips = 0;
function whenClick(){
if (dropSkips > 10) { // if the user clicked fast enough
mute(clickerEle, "click", whenClick, theOptions);
dropperEle.textContent = "You won with " + dropSkips +
" clicks per second! The button no longer changes color";
}
dropSkips = 0;
deDs ? delete deDs.numdrops : dropperEle.removeAttribute("data-numdrops");
clickerEle.setAttribute("style", "background:#"+randHex()+randHex()+randHex());
}
var theOptions = {
ALTERNATE: function(){
// whenever the main function is skipped:
deDs.numdrops = dropSkips += 1;
},
INTERVAL: 2000,
passive: true
};
listen(clickerEle, "click", whenClick, theOptions);
whenClick(); // to start changing the color
document.body.addEventListener("contextmenu", function(x){x.preventDefault()});
})();
#droppedClicks[data-numdrops]::before {
content: "Dropped " attr(data-numdrops) " clicks";
color: green;
}
Click the button below as fast as you can! You win when you are able to click the button more than ten times in a single second ().<br />
<br />
<button id="clicker"><h3>Click me</h3></button>
<div id="droppedClicks"></div>
Сохранив этот ответ, я обнаружил, что постоянные отрицательные голоса сообщества токсичных SO препятствуют запуску этого фрагмента. Поэтому вот ссылка на JSFiddle: https://jsfiddle.net/t7ymkzLx/2/
По умолчанию это ограничивает функцию до одного вызова каждые 200 мс. Чтобы изменить интервал на другое количество миллисекунд, затем optionsObject.INTERVAL
опцию optionsObject.INTERVAL
в аргументе параметров и установите для нее желаемое минимальное значение в миллисекундах между выполнениями. (Поскольку таймеры не всегда являются наиболее точными). Если у вас есть точный минимальный требуемый интервал, я бы порекомендовал вам вычесть один или два из желаемого optionsObject.INTERVAL
чтобы он всегда выполнялся, по крайней мере, когда это необходимо. Если вам нужно что-то сделать с аргументами функции удушения, когда выполнение функции optionsObject.ALTERNATE
(из-за избыточных вызовов), то используйте опцию optionsObject.ALTERNATE
. Этот "ALTERNATE" является функцией, которая вызывается немедленно вместо основной функции всякий раз, когда прекращается вызов основной функции. Например, если вы используете функцию удушения для EventTarget, но хотите preventDefault()
для пропущенных событий, используйте {ALTERNATE: function(evt){ evt.preventDefault(); }}
{ALTERNATE: function(evt){ evt.preventDefault(); }}
для объекта параметров.
debounce
. здесь, когдаonChange={debounce(this.handleOnChange, 200)}/>
, он будет вызыватьdebounce function
каждый раз. но на самом деле нам нужно вызвать функцию, возвращаемую функцией debounce.