Search code examples
javascriptreactjstypescriptframer-motion

Drag Constraints stop working after setting state in listener (Framer Motion)


I want to set window size when user resize it.

const [windowSize, setWindowsSize] = useState(window.innerWidth);

useEffect(() => {
    window.addEventListener("resize", () => {
      setWindowsSize(window.innerWidth);
    });

    return () =>
      window.removeEventListener("resize", () =>
        setWindowsSize(window.innerWidth)
      );
  }, []);

but this process totally destroys my constraints on div. When I resize window I can move this div in both directions.

<motion.div
    dragConstraints={{left: 0, right: 0}}
    initial={{ x: 0 }}
    drag="x"
    ref={ref}
    dragElastic={0.2}
    className="flex gap-8"
>

I tried many things, but nothing seems to work.

Any idea what's the problem or how to solve this issue?


Solution

  • I too was facing the same issue and after some research I got to know something that I will share.

    In React, when the component's state or props change, it triggers a re-render of the component. If the dragConstraints value is being calculated based on the component's props or state, and those values aren't changing when the window is resized, then the dragConstraints won't be updated either.

    Now, framer-motion adjusts the offset using the translate CSS property. As your window is resized, the relative positioning is changed and hence it is unable to adjust the position as we think it should according to the constraints.

    You can get around this bug for the time being by having the component forcefully re-render by defining a key. This would re-render the component and hence updating the dragConstraints accordingly.

    <motion.div
        dragConstraints={{left: 0, right: 0}}
        initial={{ x: 0 }}
        drag="x"
        ref={ref}
        dragElastic={0.2}
        className="flex gap-8"
        key={JSON.stringify(windowSize)}
    >
    </motion.div>
    

    Another method you can try is by having a dummy div which will guide the constraints for the movable div. You can achieve this by having a reference of the dummy div and use it as the dragContraints for the movable div (Nesting is necessary).

    <div ref={constraintsRef}>
        <motion.div
            dragConstraints={constraintsRef}
            initial={{ x: 0 }}
            drag="x"
            ref={ref}
            dragElastic={0.2}
            className="flex gap-8"
        >
        </motion.div>
    </div>
    

    Obviously you need to use useRef() hook here.

    Hope this would solve your concern.