如何在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
  }
});

当前回答

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

Tl;dr版本:当你要更新到观察者发送调用一个调度方法,反过来实际上会通知观察者(或执行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)
        }
    },

});

其他回答

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

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

  updateUserProfileField(fieldName) {
    const handler = throttle(value => {
      console.log(fieldName, value);
    }, 400);
    return evt => handler(evt.target.value.trim());
  }

在你的渲染方法中,你可以这样做:

<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

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

你可以用沉默

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