taye / interact.js

JavaScript drag and drop, resizing and multi-touch gestures with inertia and snapping for modern browsers (and also IE9+)
http://interactjs.io/
MIT License
12.32k stars 783 forks source link

Draggable not working with React useState and useEffect hooks #968

Closed goooooouwa closed 1 year ago

goooooouwa commented 2 years ago

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:

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>
    </>
  );
}
taye commented 2 years ago

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

taye commented 2 years ago

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)`
koji commented 2 years ago

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

Here is what I tried with interact.js

yangzhibin-git commented 1 year ago

@taye what time support in useEffect use useState

yangzhibin-git commented 1 year ago

@taye what time support in drag use useState