1
votes


I've faced with problem using React and React Material-UI components. What I need:

1) User clicks button in my component - I should add mousemove listener to the page and show ProgressBar.
2) User moves mouse - I count events, and update my ProgressBar.
3) When count of events is 50, I remove mousemove listener and hide ProgressBar.

I tried to do this with React useEffect, useState Hooks, but it does not remove listener. I don't understand, why.

Here is my code:

  const [completed, setCompleted] = React.useState(0);
  const [keyGenState, setKeyGenState] = React.useState(0);

  const updateMousePosition = ev => {
    console.log("UMP");
    setCompleted(old => old + 1);
    /*I tried to check completed value here, but it always is 0 - maybe, React re-renders component on setState..
    And I decided to take useEffect hook (see below)*/
  };

  useEffect(() => {
    console.log(completed); /*Just to understand, what happens) */
    if (completed === 49) {
        return () => {
            /*When completed value is 50, this log message appears, but mouse listener still works! */
            console.log("Finish!");
            document.removeEventListener("mousemove", updateMousePosition);
            setKeyGenState(2);
          }
    }
  }, [completed]); 

  function handleChange(e) {
    switch (e.currentTarget.id) {
        /*startKeyGen - it is ID of my Button. When user clicks it, I show ProgressBar and add mousemove listener*/
        case "startKeyGen" : {
            setKeyGenState(1);
            document.addEventListener("mousemove", updateMousePosition);
            break;}
    }
  }

/*Other logics. And finally, JSX code for my ProgressBar from Material-UI*/
<LinearProgress hidden={keyGenState !== 1 } variant="determinate" value={completed} style={{marginTop: 10}} />


It looks really strange: why React ignores removeEventListener.
Please, explain, where is my mistake.
UPD: Thanks a lot! I used useCallback hook, in this manner:

  const updateMousePosition = React.useCallback(
(ev) => {
      //console.log("Calback");
      console.log(ev.clientX);
      setCompleted(old => old + 1);
    },
  [],
);
useEffect(() => {
  //console.log(completed); /*Just to understand, what happens) */
  if (completed === 49) {
      return () => {
        /*When completed value is 50, this log message appears, but mouse listener still works! */
          console.log("Finish!");
          document.removeEventListener("mousemove", updateMousePosition);
          setKeyGenState(2);
        }
  }
}); 


But I still don't understand completely.. So, when I used useCallback with empty dependencies array, this function (updateMousePosition), will be unchanged during all "life" of my component?
And in useEffect I remove mouseListener. It is magic for me: why does useEffect ignore removing without useCallback?

1
Provide an online demo may help others understanding the problem you are facing quickly - keikai

1 Answers

2
votes

Try to use React.useCallback for updateMousePosition. Every change in your component creates new function (reference). So React.useEffect remove last updateMousePosition but doesn't remove added in handleChange.