motiondivision / motiondivision/motion
[BUG] Using a layout effect to imperatively set the position of a draggable element causes it to jump
Nobody has claimed this yet.
- Dominant language
- TypeScript
- Stars
- 33.7k
- Forks
- 1.4k
- Avg merge
- 1d 10h
- Merged PRs (30d)
- 14
Description
1. Describe the bug
I have a draggable element I'm trying to set the initial position of using a motion value x and style={{ x }}. When I do this via useLayoutEffect, I'm seeing some jumpiness.
3. IMPORTANT: Provide a CodeSandbox reproduction of the bug
Full screen: https://qjrden.csb.app
Code Sandbox: https://codesandbox.io/s/qjrden
4. Steps to reproduce
Steps to reproduce the behavior:
- Go to https://qjrden.csb.app (fullscreen version of the app)
- Wait for initial render
- Resize the window
- See the scrubber jump
5. Expected behavior
The scrubber should stay in the same place.
It only happens sometimes. If you can't reproduce, try closing tab, opening a new one and trying again.
If you change useLayoutEffect to useEffect, the bug seems to go away.
6. Video or screenshots
https://user-images.githubusercontent.com/2922250/166490664-f33ca70f-d139-428e-a546-2b5142ae6d8c.mov
Other info
The effect is to set the initial transformX property of the scrubber, and also to update its X position if the prop changes. I'm doing this with a scrubberX MotionValue and passing that into style={{ x: scrubberX }}. Let me know if there's a better way to imperatively set the position of a draggable element. I searched issues and Discord but couldn't put my finger on anything.
I know dragControls lets you call .start(event) on an event, but if I (say) just wanted to set the progress to 25%, I don't have an event, so thought it would be abusing that API to use it. But maybe I can just pass a mock/ducktyped Event into controls.start()?
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Reproduce the jump from the linked CodeSandbox by resizing the window after the initial render. Start by comparing the useLayoutEffect and useEffect paths, then inspect the draggable element's MotionValue x/style handling and dragControls behavior. Done means the scrubber retains its position during resize without requiring a mock event.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- react, typescript
- Domain
- frontend
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 35/100