使用防抖函数处理React事件

8

我有一个需要防抖处理的字段的onchange事件,我使用underscore来处理,但是当我使用防抖处理时,传递给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 是没有问题的。

这有帮助吗?https://dev59.com/M2Ag5IYBdhLWcg3w1t5a - sthzg
我尝试过使用 this.debouncedUriChangeHandler = _.debounce(this.uriChangeHandler, 500);,但是仍然出现了相同的错误。 - rcjsdev
6个回答

17

我最终采用了在github上看到的一个解决方案,对我来说效果很好。基本上,你需要将防抖函数包装在一个定制的函数debounceEventHandler中,在返回防抖函数之前保留事件。

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

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

这样做就可以消除合成事件警告。


运行得非常好!我将其添加为全局导出以便于使用。 - Jaap Weijland

3

在您的情况下,这可能会有所帮助。

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使事件无效,必须调用event.persist()

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

因此,您可以使用e => e.persist() || e代替_.clone JSBin


做到了,谢谢!你能解释一下吗?我使用了_.clone而不是_.property - rcjsdev

2

我采用了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} />;
};

0
我认为发生的情况是,在实际事件和调用你的方法之间的时间内,事件被取消了。查看\_.debounce源代码(并利用我们对去抖函数的了解),将告诉你,直到500毫秒后事件触发,你的方法才会被调用。所以你有类似这样的情况:
  1. 事件触发
  2. \_.debounce()设置一个500毫秒的超时
  3. React取消了event对象
  4. 计时器启动并调用你的事件处理程序
  5. 你在已经取消的事件上调用了event.stopPropagation()
我认为你有两个可能的解决方案:每次事件触发时(在去抖之外)调用event.stopPropagation(),或者根本不要调用它。
顺便说一下:即使使用原生事件,这仍然是一个问题。当你的处理程序实际被调用时,事件已经传播了。React只是更好地警告你已经做了一些奇怪的事情。

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

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

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

这真是太聪明了。 - Fareed Alnamrouti
这不是一个合适的解决方案。它在每个onChange事件上都调用了debounce。@xiaolin提供的解决方案才是正确的方法。 - quazar

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接