在 React 事件中使用 debouncer

Using debouncer with React event

我有一个需要去抖动的字段的 onchange 事件,我为此使用下划线,但是当我使用去抖动器时,传递给 React 处理程序的事件似乎已经结束日期。

<div className='input-field'>
  <input onChange={_.debounce(this.uriChangeHandler.bind(this), 500)} id='source_uri' type='text' name='source_uri' autofocus required />
  <label htmlFor='source_uri'>Website Link</label>
</div>

uriChangeHandler(event) {
    event.preventDefault();
    let uriField = $(event.target);
    let uri = uriField.val();
    this.setState({
        itemCreateError: null,
        loading: true
    });
    this.loadUriMetaData(uri, uriField);
}

我收到此错误:

警告:出于性能原因,此合成事件被重复使用。如果您看到这一点,则说明您是在 released/nullified 合成事件上调用 preventDefault。这是一个空操作。参见 https‍://fb‍。me/react-event-pooling 了解更多信息。

在没有去抖器的情况下使用 onchange 效果很好。

我认为正在发生的事情是,在实际事件和您的方法被调用之间的时间里,事件被取消了。查看 _.debounce source code(并使用我们所知道的去抖动函数)会告诉您,您的方法直到 500 毫秒后才会被调用,直到 after 事件触发。所以你遇到了这样的事情:

  1. 事件触发
  2. _.debounce() 设置 500 毫秒超时
  3. React 使 event 对象无效
  4. 计时器触发并调用您的事件处理程序
  5. 您在无效事件上调用 event.stopPropagation()

我认为您有两种可能的解决方案:每次触发事件时调用 event.stopPropagation()(在去抖动之外),或者根本不调用它。

旁注:即使是本地事件,这仍然是个问题。当您的处理程序实际被调用时,该事件已经传播。 React 只是在警告你你做了一些奇怪的事情方面做得更好。

在你的情况下它可能会有所帮助

class HelloWorldComponent extends React.Component {
  uriChangeHandler(target) {
    console.log(target)
  }

  render() {
    var myHandler = _.flowRight(
      _.debounce(this.uriChangeHandler.bind(this), 5e2),
      _.property('target')
    );
    return (      
      <input onChange={myHandler}  />
    );
  }
}

React.render(
  <HelloWorldComponent/>,
  document.getElementById('react_example')
);

JSBin

如果您想获得完整的事件对象,也可以使用 _.clone 而不是 _.property('target')

已编辑

为了防止 React 使事件无效,您必须按照 React doc:

中所述调用 event.persist()

If you want to access the event properties in an asynchronous way, you should call event.persist() on the event, which will remove the synthetic event from the pool and allow references to the event to be retained by user code.

因此您可以使用 e => e.persist() || e 而不是 _.clone JSBin

我最终找到了一个在 github 上看到的解决方案,它对我来说效果很好。基本上你将 debounce 函数包装在自定义函数 debounceEventHandler 中,它将在返回 debounced 函数之前保留事件。

function debounceEventHandler(...args) {
  const debounced = _.debounce(...args)
  return function(e) {
    e.persist()
    return debounced(e)
  }
}

<Input onChange={debounceEventHandler(this.handleInputChange, 150)}/>

这消除了合成事件警告

class HelloWorldComponent extends Component {
    _handleInputSearchChange = (event) => {
        event.persist();
        _.debounce((event) => {
            console.log(event.target.value);
        }, 1000)(event);
    };

    render() {
        return (
            <input onChange={this._handleInputSearchChange}  />
        );
    }
}

这里的想法是我们希望 onChange 处理程序先持久化事件然后立即去抖我们的事件处理程序,这可以通过以下代码简单地实现:

<input
onChange={_.flowRight(
  _.debounce(this.handleOnChange.bind(this), 300),
  this.persistEvent,
)}
</input>

persistEvent = e => {
  e.persist();
  e.preventDefault();
  return e;
};

handleOnChange = e => {
  console.log('event target', e.target);
  console.log('state', this.state);
  // here you can add you handler code 
}

我选择了 's answer and useMemo:

的组合
const MyComponent = () => {
  const handleChange = useMemo(() => {
    const debounced = _.debounce(e => console.log(e.target.value), 1000);
    return e => {
      e.persist();
      return debounced(e);
    };
  }, []);
  return <input onChange={handleChange} />;
};