Reputation: 390
I am trying to create a simple "wrapper" to make HTML elements "draggable".
The problem I am having is that the "useState" variable draggable
is always false within the event handler mouseMove
. The event handlers mouseDown
and mouseUp
seem to work fine, except the fact that dragging
is not properly set to true.
What am I doing wrong? Every suggestion or hint is highly appreciated!!!
PS: console.log('Dragging is set to true')
and console.log('Dragging is set to false')
are working fine! As far as understand it, all events "fire" properly. It's just that dragging
is always false within mouseMove
.
import React, { useEffect, useState, useRef } from 'react';
interface Coordinate {
x: number;
y: number;
}
const SimpleDraggableWrapper = (props: any) => {
const [offset, setOffset] = useState<Coordinate | null>(null);
const [dragging, setDragging] = useState<boolean>(false);
const element = useRef<HTMLDivElement>(null);
const mouseDown = (event: React.MouseEvent) => {
if (element.current) {
setOffset({
x: event.clientX - element.current.offsetLeft,
y: event.clientY - element.current.offsetTop,
});
setDragging(true);
console.log('Dragging set to true');
}
};
const mouseUp = (event: MouseEvent) => {
setDragging(false);
console.log('Dragging set to false');
};
const mouseMove = (event: MouseEvent) => {
console.log(dragging);
if (dragging && element.current && offset) {
element.current.style.left = `${event.clientX - offset.x}px`;
element.current.style.top = `${event.clientY - offset.y}px`;
}
};
useEffect(() => {
document.addEventListener('mouseup', mouseUp);
document.addEventListener('mousemove', mouseMove);
return () => {
document.removeEventListener('mouseup', mouseUp);
document.removeEventListener('mousemove', mouseMove);
};
}, []);
return (
<div ref={element} onMouseDown={mouseDown}>
{React.cloneElement(props.children)}
</div>
);
};
export default SimpleDraggableWrapper;
Upvotes: 0
Views: 1103
Reputation: 155
This is a common problem related to stale closures.
To know more check: https://dmitripavlutin.com/react-hooks-stale-closures/.
You can resolve this issue in two different ways:
useEffect(() => {
document.addEventListener('mouseup', mouseUp);
return () => {
document.removeEventListener('mouseup', mouseUp);
};
}, []);
useEffect(() => {
document.addEventListener('mousemove', mouseMove);
return () => {
document.removeEventListener('mousemove', mouseMove);
};
}, [dragging]);
const mouseMove = useCallback((event: MouseEvent) => {
console.log(dragging);
if (dragging && element.current && offset) {
element.current.style.left = `${event.clientX - offset.x}px`;
element.current.style.top = `${event.clientY - offset.y}px`;
}
}, [dragging]);
useEffect(() => {
document.addEventListener('mouseup', mouseUp);
return () => {
document.removeEventListener('mouseup', mouseUp);
};
}, []);
useEffect(() => {
document.addEventListener('mousemove', mouseMove);
return () => {
document.removeEventListener('mousemove', mouseMove);
};
}, [mouseMove]);
Upvotes: 1