Source code / test case example attached. Steps to reproduce:
npm install
npm run start
Start dragging and holding an event element
We identified a problem on our scheduler board when an event element is dragged and held. The issue arises if the event is moved again while an update is in progress. The element continues to display incorrectly, even though it should not. The root cause is unclear, but resolving it is essential as dragging functionality within and outside the grid is necessary. It appears to be a race condition that results in an artifact when a server update coincides with the dragging action.
I have debugged this issue and it turns out to be an issue with how you have configured the project on schedulerPro component. You were directly passing the project to the schedulerPro which is not a recommended way and is creating a new project instance on each re-render caused by the useGetEventsQuery hook. We recommend to use BryntumSchedulerProProjectModel or use useState hook to manage the project config and pass the project variable to schedulerPro component.
Approach 1:
Declare a ref for project:
const projectRef = useRef();
Now use BryntumSchedulerProProjectModel:
<BryntumSchedulerProProjectModel
ref={projectRef}
events={events}
resources={resources}
/>
// do not pass the assignments as you have already used resourceId on events
and now pass the projectRef to the SchedulerProComponent:
The useEffect you have is updating the data every 500ms. So when we start drag on an event, before, it can be dropped, the project underneath has been loaded with fresh data, the event 'in hand' no longer can be dropped on the project you have, which is why it get's 'stuck' wherever you leave it. Normally, it would just disappear since it has nowhere to go, but you have turned on the setting: constrainDragToTimeline: false so it just gets dropped into the document wherever the mouse lets go of it.
Now, to fix your error, you need to decide what should happen when you have an event 'in hand', and the data underneath the event changes. The simplest solution would be to just not allow any updates as long as the event is being dragged. To implement this, i'm using a ref to keep track of whether we should update data or not:
I'm using useRef and not useState to hold my shouldUpdateData variable because useRef doesn't re-render the component when it's value changes. Also, I'm using async await in workOrderOnDropHandlerCallback function. This is so the data doesn't update which that function is running. You might also want to turn back shouldUpdateData if the drag gets aborted etc. For that you can use the relevant functions like onDragAbort.
Lemme know if this suffices or if you want some other behavior when you're dragging the event.
I think there's a real bug here -- on the drop artifacts that's separate from what you're suggesting. Nothing is filtering or deduplicating by /id/ either events or assignments. If it was, we wouldn't see the duplicate drop artifact because immediately upon drop one would be taken out of circulation.
We're still working on testing your suggestions in our production code. useRef isn't really a practical option for us because that means we can't handle race conditions in redux. For instance, in our production code, we have a skip token that has been addressing the issue you mentioned above. However, there's a data race that can occur after drag when fetching is immediately re-enabled. In such case, it's possible for the list endpoint to return before the update endpoint does, creating another artifact on the screen. As a practical matter, we have to wait until the update operation is completed before we can restart the list operation. This means we need to be able to update the skipToken status in the state.
We're going to keep testing, and update you once we have a chance to test all the proposed changes in our redux code Monday.
In the code example you sent, what's happening under the hood is that on every data change, the project.eventStore and project.resourceStore gets a completely new dataset. You can think of it like we're doing store.data = [//new data]. It's not doing an update operation where it tries to keep the old entries and only update the new ones. So, basically we just re-render everything from scratch. That is why, upon drop, the event is not taken out of circulation, because the store has changed it's data. The event no longer belongs to that Store.
You mention that your update endpoint returns before the list endpoint, Wouldn't that be pretty easy to make sure that when update call is sent, it waits for it to finish before calling the list endpoint. Otherwise you'll be getting stale data. Well, I'm not sure how easy/difficult that is on your side.
A test case which can show your problem will of course be much better for us to understand where the problem lies, otherwise we're just throwing arrows in the dark. I hope we can get this resolved quickly for you.