0

I have a code that is supposed to add event listener on a parent component when element is clicked and remove it when the mouse is up but the removing of event listener is not working.

      const move = (e)=>{
 setPosition({top:e.clientY-50,left:e.clientX-65})

}

const handleMouseDown = (e) =>{
    backgroundRef.current.addEventListener('mousemove',(e)=>move(e))
}

const handleMouseUp = (e) =>{
    backgroundRef.current.removeEventListener('mousemove',(e)=>move(e))
}

return <div ref={iconRef} onMouseDown={handleMouseDown} onMouseUp={handleMouseUp} style={position} className='icon-container' >
    <img draggable='false' src={image} alt=''/>
    <p>{text}</p>
</div>
2
  • maybe take a look into en.wikipedia.org/wiki/Strategy_pattern Commented Jan 22, 2023 at 15:58
  • Why do you add event listeners like this in react? Also, to use removeEventListener you have to pass the same function reference, creating a new function won't work. (e)=>move(e) creates a new function Commented Jan 22, 2023 at 16:12

2 Answers 2

1

You need to pass the same function reference to both addEventListener and removeEventListener. You are currently passing a new anonymous function on each call.

const move = (e)=>{
 setPosition({top:e.clientY-50,left:e.clientX-65})

}

const handleMouseDown = (e) =>{
    backgroundRef.current.addEventListener('mousemove',move) // passing the same reference 
}

const handleMouseUp = (e) =>{
    backgroundRef.current.removeEventListener('mousemove',move) // passing the same reference 
}
Sign up to request clarification or add additional context in comments.

Comments

0

I think for removeEventListener to work in this case, in addition to passing the reference equal move for it, the functions might also need to be saved by useCallback. Because when state position changes and the component re-renders, handleMouseUp could be recreated with a new copy of move already.

Example with reference equal move and useCallback:

const Child = ({ image, text, backgroundRef }) => {
  const [position, setPosition] = React.useState(null);

  const move = React.useCallback((e) => {
    setPosition({
      top: `${e.clientY - 50}px`,
      left: `${e.clientX - 65}px`,
    });
  }, []);

  const handleMouseDown = React.useCallback(() => {
    backgroundRef.current.addEventListener('mousemove', move);
  }, [move]);

  const handleMouseUp = React.useCallback(() => {
    backgroundRef.current.removeEventListener('mousemove', move);
  }, [move]);

  return (
    <div
      onMouseDown={handleMouseDown}
      onMouseUp={handleMouseUp}
      style={position}
      className="icon-container"
    >
      <img draggable="false" src={image} alt="" />
      <p>{text}</p>
    </div>
  );
};

const Parent = () => {
  const backgroundRef = React.useRef(null);
  return (
    <section ref={backgroundRef}>
      <Child
        text="👉 Drag this"
        image="https://picsum.photos/100"
        backgroundRef={backgroundRef}
      />
    </section>
  );
};


const App = () => {
  return (
    <div>
      <Parent />
    </div>
  );
};

ReactDOM.render(<App />, document.querySelector("#root"));
section {
  position: relative;
  width: 200px;
  height: 200px;
  background-color: pink;
  display: flex;
  justify-content: center;
  align-items: center;
}

.icon-container {
  position: absolute;
  background-color: lightgreen;
  cursor: pointer;
}

.icon-container > * {
  pointer-events: none;
  user-select: none;
}
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.1.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.1.0/umd/react-dom.production.min.js"></script>

But alternatively, if the goal is to make the component follow the mouse move, it might not be necessary to host the event from the parent. The component could keep a active state to be toggled by clicks, which controls whether the dragging is active.

useCallback, addEventListener, and a ref from the parent can be omitted with this optional approach.

Example with active state:

const Child = ({ image, text }) => {
  const [active, setActive] = React.useState(false);
  const [position, setPosition] = React.useState(null);

  const move = (e) => {
    if (!active) return;
    setPosition({
      top: `${e.clientY - 50}px`,
      left: `${e.clientX - 65}px`,
    });
  };

  return (
    <div
      onMouseDown={() => setActive(true)}
      onMouseUp={() => setActive(false)}
      style={position}
      className="icon-container"
      onMouseMove={active ? move : null}
    >
      <img draggable="false" src={image} alt="" />
      <p>{text}</p>
    </div>
  );
};

const Parent = () => {
  return (
    <section>
      <Child text="👉 Drag this" image="https://picsum.photos/100" />
    </section>
  );
};

const App = () => {
  return (
    <div>
      <Parent />
    </div>
  );
};

ReactDOM.render(<App />, document.querySelector("#root"));
section {
  position: relative;
  width: 200px;
  height: 200px;
  background-color: lightblue;
  display: flex;
  justify-content: center;
  align-items: center;
}

.icon-container {
  position: absolute;
  background-color: pink;
  cursor: pointer;
}

.icon-container > * {
  pointer-events: none;
  user-select: none;
}
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.1.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.1.0/umd/react-dom.production.min.js"></script>

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.