interact.js icon indicating copy to clipboard operation
interact.js copied to clipboard

Draggable not working with React useState and useEffect hooks

Open goooooouwa opened this issue 3 years ago • 3 comments

demo: https://stackblitz.com/edit/react-ts-jeqtzy?file=App.jsx

Issue description

Draggable can not be moved if React setState method is called within a 'dragmove' event listener (which is defined within a useEffect hook).

Steps to reproduce:

  1. add an interactjs event listener for a draggable inside a useEffect hook
  2. set react useEffect dependency list as []
  3. call react setState method inside the event listener

Expected behavior

Draggable should move freely with mouse drag.

Actual behavior

Draggable get stuck and can't be moved away from it's original position.

System configuration

interact.js version: 1.10.11 react.js version: 18.1.0 Browser name and version: Chrome 101.0.4951.64 (Official Build) (x86_64) Operating System: macOS Catalina 12.3.1

Other observations

The same functionality would work under all these different scenarios:

  • Draggable component is a React class component instead of a functional component
  • useEffect dependency list is removed
  • setState method is not called
  • event listener is defined outside of a useEffect hook

Code example

import interact from "interactjs";
import React, { useEffect, useState } from "react";

export default function Draggable() {
  const [positionX, setPositionX] = useState(0);

  useEffect(() => {
    interact('.draggable').draggable({
      listeners: {
        start(event) {
          console.log(event.type, event.target)
        },
        move(event) {
          let positionx = positionX + event.dx

          event.target.style.transform =
            `translate(${positionx}px)`

          setPositionX(positionx)
        },
      }
    })
  },[]);
  return (
    <>
      <div className="draggable">Drag me</div>
    </>
  );
}

goooooouwa avatar May 19 '22 06:05 goooooouwa

Can you make a demo on https://jsfiddle.net/?

taye avatar Jun 10 '22 20:06 taye

The positionX that the listener sees within useEffect doesn't get updated by setPositionX.

It should work if you use useRef instead of useState like this:

const positionX = useRef(0)
// ...
positionX.current += event.dx
event.target.style.transform = `translate(${positionX.current}px)`

taye avatar Jul 09 '22 00:07 taye

As @taye mentioned, you should use useRef. interact.js works very well with react.

Here is what I tried with interact.js

koji avatar Sep 07 '22 19:09 koji

@taye what time support in useEffect use useState

yangzhibin-git avatar Oct 13 '22 07:10 yangzhibin-git

@taye what time support in drag use useState

yangzhibin-git avatar Oct 17 '22 09:10 yangzhibin-git