Skip to content

EventListener does not remove event listeners from the same elements that it registered them on if getSources changes #369

@jrhodkinson

Description

@jrhodkinson

When EventListener mounts, it attaches event listeners to all of the elements returned by getSources, which is either the collection of its children, or document if it has no children. When it unmounts, instead of removing these specific event listeners, it calls getSources again - but the collection of children may have changed in the meantime.

Usually when EventListener unmounts, it takes its children with it. However, in specific circumstances, this can cause the entire dash application to become non-functional.

  • If at the time of the initial call to getSources there were no children, the event listener is attached to the document
  • If children are later added dynamically, then when EventListener unmounts it will not remove the event listener from document
  • Then, when the event fires, the event handler will try to set the props on a nonexistent object
  • This corrupts dash's state and results in the incredibly difficult to diagnose error message
An object was provided as `children` instead of a component, string, or number (or list of those). Check the children property that looks something like:
{
  "props": {
    "n_events": 3
  }
}
  • I've seen this propagate into something which can make the entire app cease to function, but I can't pin down the exact steps that make that happen. When it does happen, the above error message seems to contain a much larger object.

I ran into this by putting an EventListener around dash_quill which lazy loads the Quill editor with no placeholder in the DOM. Having understood the problem, I was able to workaround the issue by wrapping the quill editor in a div rather than nesting it directly below the EventListener.

Suggestions

  1. Save the list of elements we add event listeners to and use the same list to remove them at unmount
  2. logging=True didn't give that much useful information (except show me that the event listener was still being fired even after I expected it to be unmounted, which eventually got me on the right track). Perhaps we can extend the logging to log the list of elements that we're modifying the event listeners of on mount/unmount?
  3. I found it very unintuitive that the EventListener defaulted to adding one to the document even though I passed in a non-empty list of children in Python. I know that this is documented, but it still seemed unexpected (I've been using plenty of EventListeners around my application, and never came across this functionality). In an ideal world I think defaulting to document would be an opt-in feature, but obviously this breaks backwards compatibility.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions