如何在React.js中执行debounce ?
我想撤消handleOnChange。
我尝试debounce(这。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
}
});
2022 -使用useEffect钩子
此时最好的选择是使用useEffect钩子。useEffect允许您设置一个函数,该函数可以修改状态以响应某些异步事件。debounging是异步的,因此useEffect可以很好地用于此目的。
如果从钩子返回一个函数,返回的函数将在钩子再次被调用之前被调用。这让您可以取消之前的超时,有效地解除函数。
例子
这里我们有两个状态,value和tempValue。设置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>
</>
)
}
下面是我想出的一个用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} />
}
}
这个解决方案不需要任何额外的库,它也会在用户按下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} />
在与文本输入斗争了一段时间后,我自己没有找到一个完美的解决方案,我在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组件接受您可以分配给普通输入元素的所有道具。在codeen上试试吧
我希望这也能帮助其他人,节省他们的时间。
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();
};
/**
* 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])