14

I need to throttle the mousemove event, and I follow the tips below to build the method, but doesn't work: Perform debounce in React.js

Here is my code (http://jsbin.com/binesofepo/edit?js,console,output):

class Tool extends Component {
  constructor(props) {
    super(props);
    this._onMouseMove = _.throttle(this._onMouseMove.bind(this), 1000)
  }

  render() {    
    return (

      <div ref="tool" className="tool">
        <div ref="toolBody"
             className="tool__body"
             onMouseMove={this._onMouseMove}></div>
      </div>
    )
  }
  _onMouseMove(e) {
    e.persist()
    console.log(e.screenX)
  }
}

If you keep mousemove on the tool__body, It'll get lots of below warning:

Warning: This synthetic event is reused for performance reasons. If you're seeing this, you're accessing the property screenX on a released/nullified synthetic event. This is set to null. If you must keep the original synthetic event around, use event.persist(). See fb.me/react-event-pooling for more information.

my react version: "15.0.2"

Seems e.persist() doesn't work well. Any idea? :D

Community
  • 1
  • 1
twxia
  • 1,813
  • 1
  • 15
  • 25
  • Seems pretty useful error message to me. Did you follow the suggested link https://facebook.github.io/react/docs/events.html#event-pooling? – U r s u s Jul 01 '16 at 10:27
  • yeah, so I add `e.persist()` in my `_onMouseMove`, but don't work – twxia Jul 01 '16 at 10:29

2 Answers2

25

e.persist needs to be called synchronously with the event, the handler can be called asynchronously. Here is a fix:

class Tool extends React.Component {
  constructor(props) {
    super(props);
    this._throttledMouseMove = _.throttle(this._throttledMouseMove.bind(this), 2000);
  }

  _throttledMouseMove = (e) => {
    console.log(e.screenX);
  }

  render() {    
    return (
      <div ref="tool" className="tool">
        <div ref="toolBody"
             className="tool__body"
             onMouseMove={this._onMouseMove}>
        </div>
      </div>
    )
  }

  _onMouseMove = (e) => {
    e.persist();
    this._throttledMouseMove(e);

  }
}
ReactDOM.render(<Tool/>, document.querySelector('.main'))

The relevant change is calling _onMouseMove directly from the event, and setting up a second method to actually handle event that's been throttled.

Michael Camden
  • 1,183
  • 8
  • 9
5

With hooks:

const Tool = () => {
  const onMouseMove = useMemo(() => {
    const throttled = _.throttle(e => console.log(e.screenX), 300);
    return e => {
      e.persist();
      return throttled(e);
    };
  }, []);
  return (
    <div className="tool">
      <div className="tool__body" onMouseMove={onMouseMove}>
        Content
      </div>
    </div>
  );
};
Nelu
  • 16,644
  • 10
  • 80
  • 88