不是个好主意:var SearchBox = React.createClass({方法:函数(){...},debouncedMethod: debounce(this.method, 100);});它不会工作,因为在类描述对象创建过程中,this 不是创建的对象本身.this.method 不会返回您期望的内容,因为 this 上下文不是对象本身(顺便说一句,它实际上并不存在,因为它刚刚被创建).不是个好主意:var SearchBox = React.createClass({方法:函数(){...},去抖动方法:函数(){var debounced = debounce(this.method,100);去抖动();},});这一次,您有效地创建了一个调用您的 this.method 的去抖动函数.问题是您在每次 debouncedMethod 调用时都重新创建它,因此新创建的 debounce 函数对以前的调用一无所知!随着时间的推移,您必须重复使用相同的去抖动函数,否则去抖动不会发生.不是个好主意:var SearchBox = React.createClass({debouncedMethod: debounce(function () {...},100),});这里有点棘手.该类的所有挂载实例都将共享相同的去抖动函数,而且大多数情况下这不是您想要的!请参阅 JsFiddle:3 个实例在全球范围内仅生成 1 个日志条目.您必须为每个组件实例创建一个去抖动函数,而不是每个组件实例共享的类级别的单个去抖动函数.注意 React 的事件池这是相关的,因为我们经常想要去抖动或限制 DOM 事件.在 React 中,您在回调中接收到的事件对象(即 SyntheticEvent)被池化(现在是 记录).这意味着在调用事件回调后,您收到的 SyntheticEvent 将放回具有空属性的池中,以减少 GC 压力.因此,如果您以与原始回调异步的方式访问 SyntheticEvent 属性(如节流/去抖动可能是这种情况),您访问的属性可能会被删除.如果您希望事件永远不会放回池中,您可以使用 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 允许您避免将事件放回池中.您可以在此处测试这 2 种行为:JsFiddle阅读 Julen 的回答,了解使用 persist() 和油门/去抖动的示例功能.How do you perform debounce in React.js?I want to debounce the handleOnChange.I tried with debounce(this.handleOnChange, 200) but it doesn't work.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 }}); 解决方案 2019: try hooks + promise debouncingThis is the most up to date version of how I would solve this problem. I would use:awesome-debounce-promise to debounce the async functionuse-constant to store that debounced function into the componentreact-async-hook to get the result into my componentThis is some initial wiring but you are composing primitive blocks on your own, and you can make your own custom hook so that you only need to do this once.// Generic reusable hookconst 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, };};And then you can use your hook: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> );};You will find this example running here and you should read react-async-hook documentation for more details.2018: try promise debouncingWe often want to debounce API calls to avoid flooding the backend with useless requests.In 2018, working with callbacks (Lodash/Underscore) feels bad and error-prone to me. It's easy to encounter boilerplate and concurrency issues due to API calls resolving in an arbitrary order.I've created a little library with React in mind to solve your pains: awesome-debounce-promise.This should not be more complicated than that: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 }); };}The debounced function ensures that:API calls will be debouncedthe debounced function always returns a promiseonly the last call's returned promise will resolvea single this.setState({ result }); will happen per API callEventually, you may add another trick if your component unmounts:componentWillUnmount() { this.setState = () => {};}Note that Observables (RxJS) can also be a great fit for debouncing inputs, but it's a more powerful abstraction which may be harder to learn/use correctly.< 2017: still want to use callback debouncing?The important part here is to create a single debounced (or throttled) function per component instance. You don't want to recreate the debounce (or throttle) function everytime, and you don't want either multiple instances to share the same debounced function.I'm not defining a debouncing function in this answer as it's not really relevant, but this answer will work perfectly fine with _.debounce of underscore or lodash, as well as any user-provided debouncing function.GOOD IDEA:Because debounced functions are stateful, we have to create one debounced function per component instance.ES6 (class property): recommendedclass SearchBox extends React.Component { method = debounce(() => { ... });}ES6 (class constructor)class SearchBox extends React.Component { constructor(props) { super(props); this.method = debounce(this.method.bind(this),1000); } method() { ... }}ES5var SearchBox = React.createClass({ method: function() {...}, componentWillMount: function() { this.method = debounce(this.method.bind(this),100); },});See JsFiddle: 3 instances are producing 1 log entry per instance (that makes 3 globally).NOT a good idea:var SearchBox = React.createClass({ method: function() {...}, debouncedMethod: debounce(this.method, 100);});It won't work, because during class description object creation, this is not the object created itself. this.method does not return what you expect because the this context is not the object itself (which actually does not really exist yet BTW as it is just being created).NOT a good idea:var SearchBox = React.createClass({ method: function() {...}, debouncedMethod: function() { var debounced = debounce(this.method,100); debounced(); },});This time you are effectively creating a debounced function that calls your this.method. The problem is that you are recreating it on every debouncedMethod call, so the newly created debounce function does not know anything about former calls! You must reuse the same debounced function over time or the debouncing will not happen.NOT a good idea:var SearchBox = React.createClass({ debouncedMethod: debounce(function () {...},100),});This is a little bit tricky here.All the mounted instances of the class will share the same debounced function, and most often this is not what you want!. See JsFiddle: 3 instances are producting only 1 log entry globally.You have to create a debounced function for each component instance, and not a single debounced function at the class level, shared by each component instance.Take care of React's event poolingThis is related because we often want to debounce or throttle DOM events.In React, the event objects (i.e., SyntheticEvent) that you receive in callbacks are pooled (this is now documented). This means that after the event callback has be called, the SyntheticEvent you receive will be put back in the pool with empty attributes to reduce the GC pressure.So if you access SyntheticEvent properties asynchronously to the original callback (as may be the case if you throttle/debounce), the properties you access may be erased. If you want the event to never be put back in the pool, you can use the persist() method.Without persist (default behavior: pooled event)onClick = e => { alert(`sync -> hasNativeEvent=${!!e.nativeEvent}`); setTimeout(() => { alert(`async -> hasNativeEvent=${!!e.nativeEvent}`); }, 0);};The 2nd (async) will print hasNativeEvent=false because the event properties have been cleaned up.With persistonClick = e => { e.persist(); alert(`sync -> hasNativeEvent=${!!e.nativeEvent}`); setTimeout(() => { alert(`async -> hasNativeEvent=${!!e.nativeEvent}`); }, 0);};The 2nd (async) will print hasNativeEvent=true because persist allows you to avoid putting the event back in the pool.You can test these 2 behaviors here: JsFiddleRead Julen's answer for an example of using persist() with a throttle/debounce function. 这篇关于在 React.js 中执行去抖动的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持! 上岸,阿里云!
08-22 13:38