如何进行反弹?

如何在React.js中执行debounce ?

我想撤消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
}
});
481755 次浏览

与其在debounce()中包装handleOnChange,不如在debounce()中包装回调函数中的ajax调用,从而不破坏事件对象。就像这样:

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

不受控制的组件

你可以使用# EYZ0方法

下面是一个使用下划线_.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()“诡计”。

还有JSFiddle是可用的。# EYZ1

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到React 0.12

编辑:更新的例子,以解决Sebastien Lorber提出的问题

编辑:更新的jsfiddle不使用下划线和使用纯javascript debounce。

Julen的解决方案有点难以阅读,这里有更清晰和准确的反应代码,供那些根据标题而不是问题的微小细节绊倒他的人使用。

tl;博士版本:当你更新观察者发送调用一个调度方法,而不是反过来实际上会通知观察者(或执行ajax等)

使用示例组件jsfiddle完成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)
}
},


});

你也可以使用自己编写的mixin,就像这样:

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

2019年:尝试钩子+承诺跳跃

这是我如何解决这个问题的最新版本。我会用:

这是一些最初的连接,但你是在自己组成原始块,你可以让你自己的自定义钩子,这样你只需要这样做一次。

// 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,
};
};

然后你可以用钩子:

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

您将发现这个示例运行在这里,您应该阅读react-async-hook文档以了解更多详细信息。


2018年:尝试打破承诺

我们经常希望取消API调用,以避免后端充斥着无用的请求。

在2018年,使用回调(Lodash/Underscore)对我来说感觉很糟糕,很容易出错。由于API调用以任意顺序解析,因此很容易遇到样板文件和并发问题。

我用React创建了一个小库来解决你的烦恼:awesome-debounce-promise

这个不应该比这个更复杂:

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

debound函数确保:

  • API调用将被撤销
  • debented函数总是返回一个promise
  • 只有最后一个电话的承诺才能解决问题
  • 每个API调用都会发生一个this.setState({ result });

最后,如果你的组件卸载了,你可以添加另一个技巧:

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

请注意,可见 (RxJS)也非常适合用于分解输入,但它是一个更强大的抽象,可能更难正确学习/使用。


& lt;2017:还想用回调解调吗?

这里重要的部分是为每个组件实例创建一个单独的debound(或节流)函数。您不希望每次都重新创建debounce(或throttle)函数,也不希望多个实例共享同一个debounce函数。

我没有在这个答案中定义一个debounzing函数,因为它并不真正相关,但这个答案将与_.debounce的下划线或lodash以及任何用户提供的debounzing函数完美地工作。


好主意:

因为deboned函数是有状态的,所以我们必须创建每个组件实例有一个反弹函数

# EYZ0:推荐

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

ES6(类构造函数)

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

请参阅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的debaded函数。问题是您要在每个debouncedMethod调用上重新创建它,因此新创建的debounce函数不知道以前的调用!您必须在一段时间内重用相同的回调函数,否则将不会发生回调。


这不是个好主意:

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

这有点棘手。

类的所有挂载实例都将共享相同的debked函数,这通常不是您想要的!请参阅JsFiddle: 3个实例全局只产生1个日志条目。

您必须在类级别上创建一个debked函数对于每个组件实例,而不是一个单独的debked函数,由每个组件实例共享。


负责React的事件池

这是相关的,因为我们经常想要反弹或抑制DOM事件。

在React中,你在回调中接收到的事件对象(即SyntheticEvent)被池化(现在是记录)。这意味着在事件回调被调用之后,您接收到的SyntheticEvent将被放回具有空属性的池中,以减少GC压力。

因此,如果您异步访问SyntheticEvent属性到原始回调(如果您节流/debounce可能是这种情况),您访问的属性可能会被删除。如果您希望事件永远不会被放回池中,您可以使用persist()方法。

不持久化(默认行为:合并事件)

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

第二个(异步)将打印hasNativeEvent=false,因为事件属性已被清除。

与坚持

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

第二个(异步)将打印hasNativeEvent=true,因为persist允许您避免将事件放回池中。

您可以在这里测试这两个行为:JsFiddle

阅读Julen的回答以获得使用persist()和节流/反弹函数的示例。

我正在寻找解决同一问题的解决方案,遇到了这个线程以及其他一些人,但他们有同样的问题:如果你试图做一个handleOnChange函数,你需要从事件目标的值,你会得到cannot read property value of null或一些这样的错误。在我的例子中,我还需要在debented函数中保留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)

下面是我想出的一个用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} />
}
}

如果您只需要从事件对象中获取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} />
}
}

如果你正在使用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)
}
}

然后你可以添加debounging到动作创建者,比如:

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

实际上有已经中间件,你可以从npm中得到这个。

对于throttledebounce,最好的方法是创建一个函数创建器,这样你就可以在任何地方使用它,例如:

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

为什么这将不会工作的原因,因为这将生成一个新的油门函数每次事件调用而不是使用相同的油门函数,所以基本上油门将是无用的;)

此外,如果你使用debouncethrottle,你不需要setTimeoutclearTimeout,这实际上是我们使用它们的原因:P

我发现Justin Tulk的这篇文章非常有用。经过几次尝试后,在人们认为是react/redux更正式的方式中,它显示由于React的合成事件池而失败。然后,他的解决方案使用一些内部状态来跟踪输入中更改/输入的值,并在setState之后进行回调,调用throttled/ deboundredux操作,实时显示一些结果。

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

在与文本输入斗争了一段时间后,我自己没有找到一个完美的解决方案,我在npm上发现了这个: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上试试

我希望这也能帮助其他人,节省他们的时间。

使用ES6类和15.反应x.x &lodash.debounce 我在这里使用React的参考文献,因为事件在内部丢失了这个绑定

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/lodash@4.17.5/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>

您可以使用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();

希望对你有所帮助。干杯! !

这里已经有很多好的信息了,但为了简洁。这对我很有用……

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


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

只是最近react和lodash的另一个变体。

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

你可以使用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');

这里有一个工作的TypeScript示例,供那些使用TS并想要解除async函数的人使用。

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

仅供参考

这是另一个PoC实现:

  • 没有任何库(例如lodash)用于debound
  • 使用React Hooks API

我希望它能帮助你:)

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

对于debounce,你需要在event.persist()中保留原始的合成事件。下面是使用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;

使用功能组件,您可以做到这一点-

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>
)
}
——< p >引用 - # EYZ0 - # EYZ0 < / p >

这里有点晚,但这应该有帮助。 创建这个类(它是用typescript写的,但是很容易转换成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;
}

要使用

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

一个漂亮干净的解决方案,不需要任何外部依赖:

< a href = " https://dev。to/gabe_ragland/ debouning -with- React - Hooks -jci" rel="nofollow noreferrer"> debouning with React Hooks

它使用了一个自定义加上useEffect React钩子和setTimeout / clearTimeout方法。

避免使用event.persist() -你想让React回收合成事件。我认为无论你使用类还是钩子,最干净的方法是将回调函数分成两部分:

  1. 没有deboundation的回调
  2. 使用只有调用一个debented函数来获取你需要的事件片段(这样合成的事件就可以循环使用)

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


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


<div onMouseOver={this.onMouseOver} />

功能

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


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


<div onMouseOver={this.onMouseOver} />

注意,如果你的handleMouseOver函数从组件中使用状态,你应该使用useMemo而不是useRef,并将它们作为依赖项传递,否则你将使用过时的数据(当然不适用于类)。

有一个use-debounce包,你可以使用ReactJS钩子。

从包的README:

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

从上面的示例中可以看到,它设置为每秒钟更新一次变量value(1000毫秒)。

2019:使用“useCallback”反应钩子

在尝试了许多不同的方法之后,我发现使用useCallback是解决在onChange事件中使用debounce的多次调用问题的最简单和最有效的方法。

根据Hooks API文档

useCallback返回回调的一个记忆版本,只有当其中一个依赖项发生变化时才会发生变化。

将空数组作为依赖项传递可以确保只调用一次回调。下面是一个简单的实现:

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

希望这能有所帮助!

现在在/ 2019年末< em > < / em >中有另一个React和React Native的解决方案:

react-debounce-component

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

它是一个组件,易于使用,体积小,支持广泛

例子:

enter image description here

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;

我是这个组件的创建者

下面是一个使用@Abra方法封装在函数组件中的代码片段 (我们使用fabric的UI,只是用一个简单的按钮替换它)

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

扩展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;

使用钩

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/

今天遇到了这个问题。使用setTimeoutclearTimeout解决了它。

我将给出一个你可以适应的例子:

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

你也可以在它自己的函数组件中重构它:

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

像这样使用它:

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

你试过吗?

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

这个解决方案不需要任何额外的库,它也会在用户按下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), []);

输入可以是这样的:

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

我的解决方案是基于钩子(用Typescript编写)。

我有2个主要钩子useDebouncedValueuseDebouncedCallback

第一个- useDebouncedValue

假设我们有一个搜索框,但是我们想在用户停止输入0.5秒后向服务器请求搜索结果

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

实现

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

第二个# EYZ0

它只是在你的组件范围内创建一个“debpublished”函数。

假设我们有一个带有按钮的组件,在你停止点击它后,它将显示警报500毫秒。

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


const debouncedShowAlert = useDebouncedCallback(showAlert, 500);


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

实现(注意我使用lodash/debounce作为辅助)

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

如果你不喜欢添加lodash或任何其他包:

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

使用React Hooks和响应式编程(RxJS)的React ajax debounce和cancel示例解决方案:

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;

如果你只需要在一个按钮中执行一个请求数据的debounce,提供的代码可能对你有帮助:

  1. 如果请求是对或错,创建一个函数来防止默认的条件语句

  2. 实现useState钩useEffect钩

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


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

# EYZ0:

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;

使用它在任何地方(在同一个文件中使用标识符来防止并发)像:

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

我在这个问题下找不到任何答案,提到我正在使用的方法,所以只想在这里提供一个替代解决方案,我认为这是最适合我的用例。

如果您正在使用流行的react钩子工具包库react-use,那么有一个名为useDebounce()的实用工具钩子,它以一种相当优雅的方式实现了谴责逻辑。

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


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


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

有关详细信息,请直接检查库的github页面。

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

我们需要将setter传递给防反跳方法:

下面是一个关于StackBlitz的例子:

import React from "react";
import debounce from "lodash/debounce";


export default function App() {
const [state, setState] = React.useState({
debouncedLog: ""
});


const debouncedLog = React.useCallback(
debounce((setState, log) => {
setState(prevState => ({
...prevState,
debouncedLog: log
}));
}, 500),
[]
);


const onChange = React.useCallback(({ target: { value: log } }) => {
debouncedLog(setState, log); // passing the setState along...
}, []);
return (
<div>
<input onChange={onChange} style=\{\{ outline: "1px blue solid" }} />


<pre>Debounced Value: {state.debouncedLog}</pre>
</div>
);
}


祝你好运…

简单有效
# EYZ0
# EYZ0 < / p >

import { useDebouncedCallback } from 'use-debounce';


function Input({ defaultValue }) {
const [value, setValue] = useState(defaultValue);
const debounced = useDebouncedCallback(
(value) => {
setValue(value);
},
// delay
1000
);


return (
<div>
<input defaultValue={defaultValue} onChange={(e) => debounced(e.target.value)} />
<p>Debounced value: {value}</p>
</div>
);
}

如果我们有兴趣在react.js中使用epic.js,我们可以在epic.js库中使用debounceTime rxjs/操作符。因此,我们可以在epic.js的帮助下,在react.js中使用可观察对象,而不是使用回调。

你必须使用< >强useCallback < / >强正如在这篇博客中提到的:

https://www.freecodecamp.org/news/debounce-and-throttle-in-react-with-hooks/

import React, { useCallback } from 'react';
import debounce from 'debounce'; // or another package


function App() {
...
const debouncedSave = useCallback(
debounce(x => foo(x), 1000),
[], // will be created only once initially
);
...
}

至于2021年6月,您可以简单地实现xnimorz解决方案:https://github.com/xnimorz/use-debounce

import { useState, useEffect, useRef } from "react";
// Usage
function App() {
// State and setters for ...
// Search term
const [searchTerm, setSearchTerm] = useState("");
// API search results
const [results, setResults] = useState([]);
// Searching status (whether there is pending API request)
const [isSearching, setIsSearching] = useState(false);
// Debounce search term so that it only gives us latest value ...
// ... if searchTerm has not been updated within last 500ms.
// The goal is to only have the API call fire when user stops typing ...
// ... so that we aren't hitting our API rapidly.
const debouncedSearchTerm = useDebounce(searchTerm, 500);
// Effect for API call
useEffect(
() => {
if (debouncedSearchTerm) {
setIsSearching(true);
searchCharacters(debouncedSearchTerm).then((results) => {
setIsSearching(false);
setResults(results);
});
} else {
setResults([]);
setIsSearching(false);
}
},
[debouncedSearchTerm] // Only call effect if debounced search term changes
);
return (
<div>
<input
placeholder="Search Marvel Comics"
onChange={(e) => setSearchTerm(e.target.value)}
/>
{isSearching && <div>Searching ...</div>}
{results.map((result) => (
<div key={result.id}>
<h4>{result.title}</h4>
<img
src={`${result.thumbnail.path}/portrait_incredible.${result.thumbnail.extension}`}
/>
</div>
))}
</div>
);
}
// API search function
function searchCharacters(search) {
const apiKey = "f9dfb1e8d466d36c27850bedd2047687";
return fetch(
`https://gateway.marvel.com/v1/public/comics?apikey=${apiKey}&titleStartsWith=${search}`,
{
method: "GET",
}
)
.then((r) => r.json())
.then((r) => r.data.results)
.catch((error) => {
console.error(error);
return [];
});
}
// Hook
function useDebounce(value, delay) {
// State and setters for debounced value
const [debouncedValue, setDebouncedValue] = useState(value);
useEffect(
() => {
// Update debounced value after delay
const handler = setTimeout(() => {
setDebouncedValue(value);
}, delay);
// Cancel the timeout if value changes (also on delay change or unmount)
// This is how we prevent debounced value from updating if value is changed ...
// .. within the delay period. Timeout gets cleared and restarted.
return () => {
clearTimeout(handler);
};
},
[value, delay] // Only re-call effect if value or delay changes
);
return debouncedValue;
}

有一个使用react钩子的简单方法。

步骤1:定义一个状态来维护搜索的文本

const [searchTerm, setSearchTerm] = useState('')

步骤2:使用useEffect捕获搜索Term中的任何变化

useEffect(() => {
const delayDebounceFn = setTimeout(() => {
if (searchTerm) {
// write your logic here
}
}, 400)


return () => clearTimeout(delayDebounceFn)
}, [searchTerm])

步骤3:编写一个函数来处理输入更改

function handleInputChange(value) {
if (value) {
setSearchTerm(value)
}
}


就这些!在需要时调用此方法

您可以使用引用变量来存储计时器,然后将其清除。下面是一个不使用任何第三方包在react中实现deboundation的例子

import { useState, useRef } from "react";
import "./styles.css";


export default function App() {
// Variables for debouncing
const [text, setText] = useState("");
const timer = useRef();


// Variables for throtteling
const [throttle, setThrottle] = useState(false)




const handleDebouncing = ({ target }) => {
clearTimeout(timer.current)


timer.current = setTimeout(() => {
callApi();
}, 300);


setText(target.value);
};


const handleThrottleing = () => {
callApi()


setThrottle(true)


setTimeout(() => {
setThrottle(false)
}, 2000)
}


const callApi = () => {
console.log("Calling Api");
};


return (
<div className="App">
<input type="text" onChange={handleDebouncing} />


<button onClick={handleThrottleing} disabled={throttle} >Click me to see throtteling</button>
</div>
);
}


/**
* Returns a function with the same signature of input `callback` (but without an output) that if called, smartly
* executes the `callback` in a debounced way.<br>
* There is no `delay` (to execute the `callback`) in the self-delayed tries (try = calling debounced callback). It
* will defer **only** subsequent tries (that are earlier than a minimum timeout (`delay` ms) after the latest
* execution). It also **cancels stale tries** (that have been obsoleted because of creation of newer tries during the
* same timeout).<br>
* The timeout won't be expanded! So **the subsequent execution won't be deferred more than `delay`**, at all.
* @param {Function} callback
* @param {number} [delay=167] Defaults to `167` that is equal to "10 frames at 60 Hz" (`10 * (1000 / 60) ~= 167 ms`)
* @return {Function}
*/
export function smartDebounce (callback, delay = 167) {
let minNextExecTime = 0
let timeoutId


function debounced (...args) {
const now = new Date().getTime()
if (now > minNextExecTime) { // execute immediately
minNextExecTime = now + delay // there would be at least `delay` ms between ...
callback.apply(this, args) // ... two consecutive executions
return
}
// schedule the execution:
clearTimeout(timeoutId) // unset possible previous scheduling
timeoutId = setTimeout( // set new scheduling
() => {
minNextExecTime = now + delay // there would be at least `delay` ms between ...
callback.apply(this, args) // ... two consecutive executions
},
minNextExecTime - now, // 0 <= timeout <= `delay` ... (`minNextExecTime` <= `now` + `delay`)
)
}


debounced.clear = clearTimeout.bind(null, timeoutId)


return debounced
}
/**
* Like React's `useCallback`, but will {@link smartDebounce smartly debounce} future executions.
* @param {Function} callback
* @param {[]} deps
* @param {number} [delay=167] - Defaults to `167` that is equal to "10 frames at 60 Hz" (`10 * (1000 / 60) ~= 167 ms`)
*/
export const useDebounced = (callback, deps, delay = 167) =>
useMemo(() => smartDebounce(callback, delay), [...deps, delay])

2022 -使用useEffect钩子

此时最好的选择是使用useEffect钩子。useEffect允许您设置一个函数,该函数可以修改状态以响应某些异步事件。debounging是异步的,因此useEffect可以很好地用于此目的。

如果从钩子返回一个函数,返回的函数将在钩子再次被调用之前被调用。这让您可以取消之前的超时,有效地解除函数。

例子

这里我们有两个状态,valuetempValue。设置tempValue将触发一个useEffect钩子,该钩子将启动一个1000ms的超时,该超时将调用一个函数将tempValue复制到value

钩子返回一个取消定时器设置的函数。当钩子再次被调用时(即按下另一个键),超时被取消并重置。

const DebounceDemo = () => {
const [value, setValue] = useState();
const [tempValue, setTempValue] = useState();


// This hook will set a 1000 ms timer to copy tempValue into value
// If the hook is called again, the timer will be cancelled
// This creates a debounce
useEffect(
() => {
// Wait 1000ms before copying the value of tempValue into value;
const timeout = setTimeout(() => {
setValue(tempValue);
}, 1000);


// If the hook is called again, cancel the previous timeout
// This creates a debounce instead of a delay
return () => clearTimeout(timeout);
},
// Run the hook every time the user makes a keystroke
[tempValue]
)


// Here we create an input to set tempValue.
// value will be updated 1000 ms after the hook is last called,
// i.e after the last user keystroke.
return (
<>
<input
onChange={
({ target }) => setTempValue(target.value)
}
/>
<p>{ value }</p>
</>
)
}