New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix incorrect Reorder.Item order calculation #2231
Conversation
Awesome work, thx! |
Hey @mattgperry, |
Let me share what I found for now. I was investigating another race condition where It seems to be introduced in #2025. Before it lands, calling I did not dig into how it is related to Reorder, but rolling back it somehow seems to solve this issue. UPDATE: I could reproduce this |
That's great sleuthing 🕵️♂️ In my opinion it would still be a good thing to remove this indirection from the code anyway, regardless of what causes the execution of the two hooks to be reordered. Passing the layout measurements between two hooks via a ref seems unnecessary and makes the code brittle to an order of execution that it doesn't control. (Which is not at all to say that tracking down the cause of what you mention is not worthwhile as well. It sounds like fixing that would have wider benefits to other APIs as well. 😀) |
I agree that this PR's approach looks more robust. One thing I can come up with regarding the existing approach is for the better performance? but not quite sure..😅 While I was investigating it further, I found that It happens randomly when you drag an item, stop the cursor for a while, and drop it as attached bellow: reorder-stuck.movI have confirmed that this still happens on this PR, and the fix will be soon ready to submit! UPDATE: #2264 is the one. |
@chuganzy: I've tried your patch from #2264. For clarity, I don't think it fixes the same issue that is described here. The inertial animation code you have modified is only invoked when an item is dropped (ie. on mouseup), but the issue I have tried to fix happens while the item is being dragged (ie. while the mouse button is down). |
Ah, my bad, I was not clear enough🙏 Yes, this PR is still needed. Mine only fixes the end of the animation as you mentioned. |
Hey @mattgperry, |
Any updates here? |
Are there any blockers to merge this PR? |
I messaged the maintainer on Twitter (fine, X). The hold-up is that this PR isn't mergeable without an integration test. I don't have time immediately to work on this, but I'll try in the next few weeks. Alternatively others could feel free to try and write this. |
Given that this PR fixes what I would consider a major bug on an important feature of the library, and that writing an integration test for this looks very tricky (I have no idea how to approach it at least) maybe we should merge this soon? Or maybe @obeattie or @mattgperry can give it another shot? It has been almost 3 months that the fix was proposed, it feels wasteful not to merge it <3 |
It would be great to see this move forward, it's a shame to not see the benefit of any of the work being done on motion due to such a major feature being broken despite a solution existing. Appreciate @nick-keller for persisting with this 🙌 |
Hi! JFYI: I just opened a PR that cherry-picks the changes here, and adds integration test! |
Merged via #2359 |
Problem
I've been experiencing an issue with
Reorder
lately, where items can become "stuck" while being dragged. It's a little hard to describe in words, but here's a recording of an example from an app I am building:As you can see here, the "Sydney" item is first dragged over "Vancouver", and then "London" without a problem. But when "Vancouver" is then dragged backwards over "London", "Vancouver" also moves, even though the user hasn't moved the cursor that far to the left yet. As the dragging continues, it becomes totally impossible to reorder the items. This is reliably reproducible in my example, but not in more trivial examples, leading to much frustration.
Cause
The underlying issue is that
Reorder.Group
has an incorrect view of the current order of its items on the page. To determine that order, relies on the layout measurements it is passed by its childReorder.Item
s via aregisterItem
callback. But the code inReorder.Item
to pass the layout measurements has a race condition. The intended flow seems to be:onLayoutMeasure
handlerregisterItem
callbackBut there is a race condition between 1 and 3, and sometimes 3 can execute before 1. I'm not familiar enough with React's internals to understand why the two hooks execute in a non-deterministic order, but they do. And when that happens, stale layout values from the last render are passed to
registerItem
and theReorder.Group
gets an incorrect order.Solution
Instead of using a ref to temporarily hold the layout information, I'm just invoking
registerItem
directly in theonLayoutMeasure
callback.With this patch, the issue disappears and the reordering works as you would expect:
I've also tried this fix with the example in #2101. I can reproduce that issue pretty easily with framer-motion 10.12.21, and it seems fixed with this patch. Perhaps some of the other open Reorder bugs can be attributed to this, too. (Fixes #2101).
Notes
There aren't any comments in the code to explain why the extra indirection with the ref is necessary, and the commit that introduced the code doesn't contain any helpful description either. Being unsure why that choice was made, I'm left wondering if it was meant to solve a problem I am now overlooking. But, things seem to work just as well as before with my change, and the bug is fixed too.