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

当前回答

看点:

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

其他回答

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

对于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

你必须使用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
    );
    ...
}

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

});