nuclearpasta / react-native-drax

A drag-and-drop system for React Native
MIT License
554 stars 69 forks source link

Issue with elements set to both Dragging and Receiving within a separate Receiving Container #79

Open ajstokar opened 3 years ago

ajstokar commented 3 years ago

Hi @lafiosca !

While creating a Snack for the List dragging issue, I found this one. When Items are dragged over Box's, the receiving style is applied. However, when Items are dragged over other Items, the receiving style is not applied. Let me know where I went wrong or if this may be an issue.

https://snack.expo.io/@adamommyx/drax-drag-and-receive-test

lafiosca commented 3 years ago

Thanks for reproducing this very clearly. One thing I noticed/remembered is that when you are nesting DraxViews, you should specify isParent on the container DraxViews. This causes them to create a DraxSubprovider beneath them which links their nested Drax descendants to their position. Without this, if the container layout changes but the contained item layout does not change, the global positions of the nested views may become incorrect in the registry.

I was hopeful that specifying isParent would be enough to fix this issue, but it is not. Without digging into the code, it seems clear to me that the container DraxView (box) is taking precedence over its nested views (items) in the target calculation algorithm. The reason I suspect this is that if I set isReceptive={false} on the box, the items light up correctly when being dragged over.

I have not looked at the logic in a long time, but this is not how I remember the intended behavior to be. The code can be found in this section: https://github.com/nuclearpasta/react-native-drax/blob/051295857fd840e38eb79ded9edabee31f8d7178/src/hooks/useDraxRegistry.ts#L148-L224

It iterates through all of the views in the registry and checks if the current drag is in them, if they are receptive or monitoring. It returns the most recently registered receiver that the drag is over. I believe my thought at the time was that the nested children would be registered after their parent container, so they would take precedence here. But it seems, at least in this example, that this is not the case. I am not sure if this was always broken, if it's case-by-case, or if something changed between versions of RN. I think perhaps the logic needs to be improved to more explicitly track view ancestry and give precedence to nested receivers.

I am sorry to say I do not know of any quick workaround off the top of my head. You could perhaps hack something together using monitors. Regrettably I do not know when I will have availability to return to this code more deeply.

ajstokar commented 3 years ago

Thank you so much for the write up. I also tried isParent={true} and it didn't help unfortunately. This use case is similar to the iOS app groups where you can drag items between each other AND within each other. If it gives you any extra motivation, I haven't found ANY other libraries that come close to what you've built. It's really impressive!

lafiosca commented 3 years ago

Thank you for the kind words. I am proud of what I've built so far, but I also see that it has so much further to go. With everything going on in my life and business over the past year, I have not been able to afford the time and energy to give this library the attention it deserves, and I feel a lot of guilt over that. I intentionally released it as a 0.x prerelease version because I knew we would gather feedback to learn how well the approach fit user needs, and it's possible the infrastructure could change significantly when I go back through it. All of the recent activity on the repo has been making me very antsy to find time though, so I am going to be looking for any opportunity to fit the Drax work into my primary work as soon as I can.

ajstokar commented 3 years ago

Good news, I fixed the other issue so this is the last one before we can feel good about using the library! Is there any other guidance / workaround you can provide to handle this one? It seems that when I hot reload and modify the contents of the parent DraxView, it works as intended. But when the DraxScrollView re-renders, it goes back to only receiving in the parent views.

lafiosca commented 3 years ago

The only workaround that came to mind was to use monitor events. Monitors are sort of like receivers, but they always receive monitor events even if they are not the targeted receiver of the drag. Theoretically you could make your nested views monitor drags over them and communicate up the chain that the outer container should turn receptive={false} during them. Offhand, I do not know if changing the receptive flag mid-drag would cause unpredictable behavior though.

Ultimately the library needs to fix this behavior, or at least provide a way to explicitly specify the precedence of receivers.