bevacqua/react-dragula

Getting Uncaught DOMException: Failed to execute 'removeChild' on 'Node' when rerendering component

Opened this issue · 12 comments

Hi,

I am getting this error: Uncaught DOMException: Failed to execute 'removeChild' on 'Node': The node to be removed is not a child of this node, when my react component is rerendered after the drag is completed.

It is probably because the DOM was changed without telling React, and it can no longer find the dragged item in the right place. Is there a workaround for me for this issue? Thank you.

Same problem. I think react-dragula needs to provide a method to cancel everything it has done.
I have tried drake.cancel();, but not working.

Same problem here. Can we check what should be done for this ?

drake.cancel(true); works fine.

You saved my day :).
Thanks a lot :)

Can you expand on how drake.cancel(true); was correlated with the drag and dropping in your code in order to remove the error and still render successfully?

The problem was that the actual DOM structure changed after drag and drop but the virtual DOM (react DOM) doesn't aware of that. So after drag and drop if I will set the state with this.setState() which will be going to change the DOM. It gave me error i.e children dom structure changed or mismatch (don't remember exactly error).If we do drake.cancel(true); the dragula doesn't change the actual DOM and setState works fine and mimic the same effect if I will change my state in the right way just after drake.cancle :)

Hope it helps you to understand it :)

This works but creates an effect where the item jumps back to its original location and then to the new target location. Is there a recommended way to fix that?

If anybody else comes across this, I used the following in my drop callback, which helped a lot:

el.setAttribute("style", `${el.style.cssText};display: none;`);
cardDrake.cancel(true)

Before canceling, I add the display: none; style to the el. This has the effect of hiding the original element so it doesn't pop back into its original place until React re-renders. It is much less noticeable now. It does have the downside that if the logic to update the data on the backend fails, the user has to refresh before they see it again. I suppose that could be fixed in a failure callback though.

One more update...I just got super confused when moving my item caused it to disappear lol! You only want to hide it if you are moving the item to a new container. If it is staying in the same container, it will remain hidden since react won't render a new element, it just moves it instead. Back to the original problem... (or remove the hide in a callback, which is what I will probably do).

xy-ty commented

why I use drake.cancel(true) it do not work

y0n4 commented

One more update...I just got super confused when moving my item caused it to disappear lol! You only want to hide it if you are moving the item to a new container. If it is staying in the same container, it will remain hidden since react won't render a new element, it just moves it instead. Back to the original problem... (or remove the hide in a callback, which is what I will probably do).

I was experiencing exactly the same problems that @brandoncc was experiencing. While drake.cancel(true) in the drop listener did prevent that error message to pop up, the next problem was that the dragged element goes back to the original container before switching to the next container. I tried to do some modification with styling as well but I was also experiencing the element disappearing.

What worked for me was I did optimistic ui and in this case we already assume that the dragged element will stay onto the targeted container the moment we know what element is being dragged and what container is being targeted. So by having to setState of dragged element to somehow be under the targeted container category in the drop listener callback right before drake.cancel(true) is called, will keep the element in place. This allows the page to re-render and keep the dragged element to stay in its targeted container, prevent the error message to pop up and even stop the page from crashing (at least in my case).

Example

const drake = Dragula(containers, { revertOnSpill: true });

drake.on('drop', (el, target) => {
  // ** sets optimistic ui **
  this.setState({
    draggedElement: { id: el.id, group: target.id }
  })
  
  /**
  * add any action here
  */

  drake.cancel(true);
}

I got the same DOMException. My solution for this was to find a container component, which contains the dragged item and the containers, and I added a key prop to that component. I always update the key prop when I would like the draggable items in the containers to get updated, and by using a new key value the whole component will be re-rendered. It worked for me to solve the DOMException.