将 debouncer 与 React 事件一起使用

Posted

技术标签:

【中文标题】将 debouncer 与 React 事件一起使用【英文标题】:Using debouncer with React event 【发布时间】:2016-05-27 21:08:05 【问题描述】:

我有一个需要去抖动的字段的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);

我收到此错误:

警告:出于性能原因,此合成事件会被重复使用。如果您看到这一点,则表示您正在对已发布/无效的合成事件调用 preventDefault。这是一个无操作。有关详细信息,请参阅 https‍://fb‍.me/react-event-pooling。

使用onchange 而不使用去抖器可以正常工作。

【问题讨论】:

这有帮助吗? ***.com/q/23123138/870769 不,我用this.debouncedUriChangeHandler = _.debounce(this.uriChangeHandler, 500); 尝试过,但得到了同样的错误 【参考方案1】:

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

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


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

这摆脱了合成事件警告

【讨论】:

像魅力一样工作!我将此添加为全局导出以方便使用。【参考方案2】:

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

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 使事件无效,您必须调用 event.persist(),如 React doc 所述:

如果您想以异步方式访问事件属性,您应该在事件上调用 event.persist(),这将从池中删除合成事件并允许用户代码保留对事件的引用。

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

【讨论】:

成功了,谢谢!你能解释一下怎么做吗?我使用了_.clone 位而不是_.property【参考方案3】:

我选择了xiaolin的答案和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 />;
;

【讨论】:

【参考方案4】:

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

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

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

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

【讨论】:

【参考方案5】:
class HelloWorldComponent extends Component 
    _handleInputSearchChange = (event) => 
        event.persist();
        _.debounce((event) => 
            console.log(event.target.value);
        , 1000)(event);
    ;

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

【讨论】:

这太聪明了 这不是一个合适的解决方案。它在每个 onChange 事件上调用 debounce。 @xiaolin 的解决方案是正确的方法。【参考方案6】:

这里的想法是我们希望 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 

【讨论】:

以上是关于将 debouncer 与 React 事件一起使用的主要内容,如果未能解决你的问题,请参考以下文章

React中事件节流防抖

如何将 React Context API 与 useReducer 一起使用以遵循与 Redux 类似的风格 [关闭]

react-autosuggest 与 debounce 和 distinctUntilChanged

防抖(Debounce)与节流( throttle)区别

无法使 react-hot-loader 和 webpack-dev-server 与 react-router 一起工作

如何将 react-datepicker 与 redux 表单一起使用?