在React.js中执行去抖动

Che*_*ola 443 javascript reactjs

你如何在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
  }
});
Run Code Online (Sandbox Code Playgroud)

Seb*_*ber 794

在2018年:尝试承诺debouncing

我们经常要去除API调用,以避免使用无用的请求充斥后端.

在2018年,使用回调(Lodash/Underscore)感觉不好并且容易出错.由于API调用以任意顺序解析,因此很容易遇到样板和并发问题.

我已经创建了一个带React的小库来解决你的痛苦:真棒 - 去抖 - 承诺.

这应该不是那么复杂:

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

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

  const search = useAsync(
    async () => {
      if (inputText.length === 0) {
        return [];
      } else {
        return debouncedSearchStarwarsHero(inputText);
      }
    },
    // Ensure a new request is made everytime the text changes (even if it's debounced)
    [inputText]
  );

  // Return everything needed for the hook consumer
  return {
    inputText,
    setInputText,
    search,
  };
};
Run Code Online (Sandbox Code Playgroud)

去抖动功能可确保:

  • API调用将被去除
  • debounced函数总是返回一个promise
  • 只有最后一个电话的退回承诺才能解决
  • 一个this.setState({ result });将每个API调用发生

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

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

请注意,Observables(RxJS)也非常适合去抖动输入,但它是一种更强大的抽象,可能更难以正确学习/使用.


还想使用回调去抖动吗?

这里的重要部分是为每个组件实例创建一个去抖(或限制)函数.您不希望每次都重新创建去抖(或节流)功能,并且您不希望多个实例共享相同的去抖动功能.

我没有在这个答案中定义一个去抖函数,因为它并不是真正相关的,但是这个答案_.debounce对于下划线或lodash,以及任何用户提供的去抖动功能都可以完美地完成.


好主意:

因为去抖动函数是有状态的,所以我们必须为每个组件实例创建一个去抖动函数.

ES6(类属性):推荐

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 });
  };
}
Run Code Online (Sandbox Code Playgroud)

ES6(类构造函数)

componentWillUnmount() {
  this.setState = () => {};
}
Run Code Online (Sandbox Code Playgroud)

ES5

class SearchBox extends React.Component {
    method = debounce(() => { 
      ...
    });
}
Run Code Online (Sandbox Code Playgroud)

请参阅JsFiddle:3个实例每个实例生成1个日志条目(全局生成3个).


不是一个好主意:

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

它不起作用,因为在类描述对象创建期间,this不是自己创建的对象.this.method不会返回您期望的内容,因为this上下文不是对象本身(实际上并不存在BTW,因为它只是被创建).


不是一个好主意:

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

这次你有效地创建了一个调用你的debounced函数this.method.问题是你在每次debouncedMethod通话时都在重新创建它,所以新创建的去抖功能对以前的通话一无所知!您必须重复使用相同的去抖功能,否则不会发生去抖动.


不是一个好主意:

var SearchBox = React.createClass({
  method: function() {...},
  debouncedMethod: debounce(this.method, 100);
});
Run Code Online (Sandbox Code Playgroud)

这在这里有点棘手.

该类的所有已安装实例将共享相同的去抖功能,并且通常这不是您想要的!请参阅JsFiddle:3个实例仅在全局范围内生成1个日志条目.

您必须为每个组件实例创建一个去抖动函数,而不是在每个组件实例共享的类级别的单个去抖动函数.


照顾React的事件池

这是相关的,因为我们经常想要去抖动或限制DOM事件.

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

因此,如果您SyntheticEvent与原始回调异步访问属性(如果您加油/去抖动可能就是这种情况),则您访问的属性可能会被删除.如果您希望永远不会将事件放回池中,则可以使用该persist()方法.

没有持久化(默认行为:池事件)

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

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

坚持不懈

var SearchBox = React.createClass({
  debouncedMethod: debounce(function () {...},100),
});
Run Code Online (Sandbox Code Playgroud)

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

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

阅读Julen的答案,了解使用persist()油门/去抖功能的示例.

  • 不要忘记取消`componentWillUnmount`中的debounced方法:`this.method.cancel()` - 否则它可能想要在未安装的组件上设置setState. (23认同)
  • 请注意,在ES6中,不是在构造函数中定义您的方法(感觉很奇怪),您可以在您的顶层执行`handleOnChange = debounce((e)=> {/*onChange handler code*/,timeout)`类.您仍然在有效地设置实例成员,但它看起来更像一个普通的方法定义.如果你还没有定义一个`构造函数`,则不需要它.我想这主要是一种风格偏好. (8认同)
  • @JonasKello你无法在无状态组件中去抖动,因为去抖动函数实际上是有状态的.你需要一个有状态的组件来保存去抖动功能,但是你可以根据需要调用一个已经去抖动的无状态组件. (4认同)
  • 非常好的答案,这非常适合在停止输入后将表单字段状态设置为"交互"几秒钟,然后能够在表单提交或onBlur上取消 (3认同)
  • 为什么所有答案都包含_.debounce而不是编写函数?该功能需要整个库吗? (2认同)

jul*_*len 212

不受控制的组件

您可以使用该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} />
    );
  }

});
Run Code Online (Sandbox Code Playgroud)

编辑:看到这个JSFiddle


受控组件

更新:上面的示例显示了一个不受控制的组件.我一直使用受控元素,所以这是上面的另一个例子,但没有使用event.persist()"技巧".

的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);
Run Code Online (Sandbox Code Playgroud)

编辑:更新的示例和JSFiddles到React 0.12

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

编辑:使用jsfiddle更新,不使用下划线并使用普通的javascript去抖.

  • 虽然这是一个很好的答案,但我不推荐使用`persist`,尤其是当有很多事件时,例如`mousemove`.我看到代码变得完全没有响应.在事件调用中从本机事件中提取所需数据,然后仅使用数据而不是事件本身调用去抖/限制函数,效率要高得多.没有必要坚持这样的事件 (4认同)
  • 如果您在DOM中多次挂载组件,这是非常危险的,请参阅http://stackoverflow.com/questions/23123138/perform-debounce-in-react-js/28046731#28046731 (2认同)

小智 22

可以有一种使用React hooks 的简单方法。

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

const [searchTerm, setSearchTerm] = useState('')
Run Code Online (Sandbox Code Playgroud)

步骤 2:使用 useEffect 捕获任何变化searchTerm

const [searchTerm, setSearchTerm] = useState('')
Run Code Online (Sandbox Code Playgroud)

第三步:编写一个函数来处理输入变化

function handleInputChange(value) {
  if (value) {
    setSearchTerm(value)
  }
}
Run Code Online (Sandbox Code Playgroud)

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


sup*_*ary 19

2022 - 使用 useEffect 钩子

\n

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

\n

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

\n

例子

\n

这里我们有两个状态valuetempValue。设置tempValue将触发一个useEffect钩子,该钩子将启动 1000\xc2\xa0ms 超时,该超时将调用一个函数来复制tempValuevalue.

\n

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

\n
const DebounceDemo = () => {\n  const [value, setValue] = useState();\n  const [tempValue, setTempValue] = useState();\n\n  // This hook will set a 1000 ms timer to copy tempValue into value\n  // If the hook is called again, the timer will be cancelled\n  // This creates a debounce\n  useEffect(\n    () => {\n      // Wait 1000 ms before copying the value of tempValue into value;\n      const timeout = setTimeout(() => {\n        setValue(tempValue);\n      }, 1000);\n\n      // If the hook is called again, cancel the previous timeout\n      // This creates a debounce instead of a delay\n      return () => clearTimeout(timeout);\n    },\n    // Run the hook every time the user makes a keystroke\n    [tempValue]\n  )\n\n  // Here we create an input to set tempValue.\n  // value will be updated 1000 ms after the hook is last called,\n  // i.e after the last user keystroke.\n  return (\n    <>\n      <input\n        onChange={\n          ({ target }) => setTempValue(target.value)\n        }\n      />\n      <p>{ value }</p>\n    </>\n  )\n}\n
Run Code Online (Sandbox Code Playgroud)\n


Yur*_*ura 15

如果您需要从事件对象获取DOM输入元素,那么解决方案就更简单了 - 只需使用即可ref.请注意,这需要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} />
    }
}
Run Code Online (Sandbox Code Playgroud)

  • defaultValue是我想要的!非常感谢你 :) (2认同)

rac*_*mic 13

我发现贾斯汀图尔克的这篇文章非常有帮助.一对夫妇的尝试,在什么人会认为是与反应/终极版更正式的方式后,它表明,由于失败作出反应的合成事件池.然后他的解决方案使用一些内部状态来跟踪输入中更改/输入的值,然后使用回调setState调用限制/去抖动的redux操作,该操作实时显示一些结果.

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}
        />
    )
  }
}
Run Code Online (Sandbox Code Playgroud)


Hoo*_*ari 11

在对文本输入进行了一段时间的努力而没有找到完美的解决方案之后,我在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);
Run Code Online (Sandbox Code Playgroud)

DebounceInput组件接受您可以分配给普通输入元素的所有道具.在codepen上尝试一下

我希望它也可以帮助别人并节省一些时间.


pie*_*e6k 9

我的解决方案是基于钩子的(用 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)} />
}
Run Code Online (Sandbox Code Playgroud)

执行

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;
}
Run Code Online (Sandbox Code Playgroud)

第二 useDebouncedCallback

它只是在您的组件范围内创建一个“去抖动”函数。

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

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

  const debouncedShowAlert = useDebouncedCallback(showAlert, 500);

  return <button onClick={debouncedShowAlert}>Click</button>
}
Run Code Online (Sandbox Code Playgroud)

实现(注意我使用 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;
}
Run Code Online (Sandbox Code Playgroud)

  • 喜欢这个解决方案,因为它不需要新的依赖项 (4认同)

Art*_*Art 8

有一个use-debounce包可以与 ReactJS 钩子一起使用。

从包的自述文件:

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>
  );
}
Run Code Online (Sandbox Code Playgroud)

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

  • 仍然是2021年1月的最佳选择 (2认同)

Mat*_*att 7

如果您使用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)
  }
}
Run Code Online (Sandbox Code Playgroud)

然后,您可以向操作创建者添加去抖动,例如:

export default debouncedAction = (payload) => ({
  type : 'DEBOUNCED_ACTION',
  payload : payload,
  meta : {debounce : 300}
}
Run Code Online (Sandbox Code Playgroud)

实际上已经有中间件你可以下午npm为你做这件事.


cha*_*ele 7

这里有很多好消息,但要简洁明了.这对我有用......

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

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


STE*_*EEL 6

使用ES6 CLASS和React 15.xx&lodash.debounce我在这里使用React的refs,因为事件在内部丢失了这个绑定.

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')
);
Run Code Online (Sandbox Code Playgroud)
<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>
Run Code Online (Sandbox Code Playgroud)


Din*_*lal 6

您可以使用Lodash防反跳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);
}
Run Code Online (Sandbox Code Playgroud)

您也可以使用以下方法取消反跳方法。

this.debounceHandleUpdate.cancel();
Run Code Online (Sandbox Code Playgroud)

希望对您有帮助。干杯!!


Sam*_*ale 6

2019:使用'useCallback'react钩子

尝试许多不同的方法后,我发现使用useCallback是最简单和解决在使用的多个呼叫问题最有效debounce的范围内onChange活动。

根据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();
 };
Run Code Online (Sandbox Code Playgroud)

希望这可以帮助!

  • 如果您使用钩子,这是一个很好的解决方案。你让我不再受挫。谢谢! (8认同)
  • 您能否解释一下为什么会发生多次通话?`debounce()` 是否不认为 `onChange()` 回调是相同的回调方法? (2认同)

Xin*_*nan 6

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

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

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

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

return <input onChange={({ currentTarget }) => setQuery(currentTarget.value)} />
Run Code Online (Sandbox Code Playgroud)

详情请直接查看lib的github页面。

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


OrA*_*yag 6

截至 2021 年 6 月,您可以简单地实施 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 500 ms.
  // 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 recall effect if value or delay changes
  );
  return debouncedValue;
}
Run Code Online (Sandbox Code Playgroud)


iti*_*skj 5

费耶

这是另一个PoC实施:

  • 没有任何用于反跳的库(例如lodash)
  • 使用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}
    />
  );
}
Run Code Online (Sandbox Code Playgroud)


Moh*_*ere 5

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;
Run Code Online (Sandbox Code Playgroud)

使用功能组件,您可以执行以下操作-

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>
  )
}
Run Code Online (Sandbox Code Playgroud)

参考--https : //gist.github.com/elijahmanor/08fc6c8468c994c844213e4a4344a709-https://blog.revathskumar.com/2016/02/reactjs-using-debounce-in-react-components.html


Bru*_*ano 5

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

使用 React Hooks 去抖

它使用自定义加上 useEffect React 挂钩和setTimeout/clearTimeout方法。


Reb*_*ebs 5

现在在2019 年末为 React 和 React Native 提供了另一种解决方案:

反应去抖动组件

<input>
<Debounce ms={500}>
  <List/>
</Debounce>
Run Code Online (Sandbox Code Playgroud)

它是一个组件,易于使用,小巧且支持widley

例子:

在此处输入图片说明

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;
Run Code Online (Sandbox Code Playgroud)

*我是这个组件的创建者


小智 2

不要将handleOnChange 包装在debounce() 中,而是将Ajax 调用包装在debounce 内的回调函数内,从而不会破坏事件对象。

所以像这样:

handleOnChange: function (event) {
   debounce(
     $.ajax({})
  , 250);
}
Run Code Online (Sandbox Code Playgroud)

  • 因为事件对象不是不可变的并且会被 ReactJS 销毁,所以即使您包装并实现闭包捕获,代码也会失败。 (4认同)