Event listener onMouseMove in react hooks: event object is undefined or clientX/Y are null

Viewed 3131

I am trying to bind a mouse move event to the thing. But what ever I do, either e is empty or e.clientX and Y is null.

const App = (props) => {
    const [move, setMove] = useState({"ix": 0, "iy": 0});

    const handleMove = (e) => {
        console.log(e);
    }

    return (
        <div onMouseMove={handleMove} width="500" height="500"></div> //e exists but clientX is null
        <div onMouseMove={() => handleMove()} width="500" height="500"></div> //e undefined 
    );
}

console log:

{…}
_dispatchInstances: null
_dispatchListeners: null
_targetInst: null
altKey: 
bubbles: 
button: 
buttons:
cancelable: 
clientX: null
clientY: null

additionally I am flooded with this warning, which I don't understand even after reading the provided link

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

3 Answers

Since the synthetic event is pooled, all event properties are nullified after the event callback is invoked.

However, event properties can still be accessed like:

const handleMove = (e) => {
  console.log(e) // nullified object
  console.log(e.clientX); // has value
}

Or using event.persist() to remove this synthetic event from the pool.

const handleMove = (e) => {
  e.persist();
  console.log(e); // has value for clientX, etc
}

What happens there, is that you're trying to console.log() a synthetic event. And React gives you that error. Which is completely reasonable, here's why, as React documentation states:

This synthetic event is reused for performance reasons. If you're seeing this, you're accessing the property pageY on a released/nullified synthetic event. This is set to null. If you must keep the original synthetic event around, use event.persist().

So, a synthetic event object is reused by React. That's why, all the fields, will be nullified at the end of every event handler. It leads us to the the rule of thumb:

You can't access synthetic event object is an asynchronous way.

This is why React was complaining, because you tried to access synthetic event object in your console, after synthetic event was nullified. The ways to get around that, is to either call persist function of synthetic event to persist all the fields or just use the fields you need right away.

    const handleMove = (e) => {
        console.log(e); // <~ will cause error to come up
        console.log(e.pageY) // <~ no error
    }

    const handleMovePersist = (e) => {
        e.persist()
        console.log(e) // <~ no error, because synthetic event is persisted
    }

Hope it helps :)

You are not passing the event.

<div onMouseMove={(e) => handleMove(e)} width="500" height="500"></div>

// Get a hook function
const {useState} = React;

  

function App() {

  const [move, setMove] = useState({ ix: 0, iy: 0 });
  
  const getMousePos = e => {
    return { x: e.clientX, y: e.clientY };
  };
  
  const handleMove = e => {
    console.log(getMousePos(e));
  };
  
  
  return (
    <div
        onMouseMove={e => handleMove(e)}
        style={{ backgroundColor: "blue", width: 500, height: 500 }}
      >
      </div>
  );
}
// Render it
ReactDOM.render(
  <App />,
  document.getElementById("react")
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
<div id="react"></div>

Related