hperrin / svelte-material-ui

Svelte Material UI Components
https://sveltematerialui.com/
Apache License 2.0
3.3k stars 286 forks source link

Tooltip crashes when inserting elements in `onDestroy` #453

Open tgolsson opened 2 years ago

tgolsson commented 2 years ago

Describe the bug

I'm seeing crashes when a component using multiple Wrapper and Tooltip is being destroyed. The error message is:

Failed to execute 'insertBefore' on 'Node': The node before which the new node is to be inserted is not a child of this node.

and occurs here: https://github.com/hperrin/svelte-material-ui/blob/master/packages/tooltip/src/Tooltip.svelte#L320

It seems to me like it's related to elements being inside {#if} blocks. The code is about the following

<Wrapper>
    <Button>...</Button>
    <Tooltip>Foo</Tooltip>
</Wrapper>

<Wrapper>
    <Button>...</Button>
    <Tooltip>Foo</Tooltip>
</Wrapper>

{#if someProp}
    <Button>...</Button>
{/if}
<Button on:click={toggleSomeProp}>...</Button>

This works fine until the component gets unmounted, at which point Tooltip will crash in the second Wrapper. There's no crash with a single Wrapper, or with only one tooltip.

This is the full callstack:

Tooltip.svelte?4208:476 Uncaught (in promise) DOMException: Failed to execute 'insertBefore' on 'Node': The node before which the new node is to be inserted is not a child of this node.
    at eval (webpack-internal:///../../../node_modules/@smui/tooltip/dist/Tooltip.svelte:445:36)
    at run (webpack-internal:///../../../node_modules/svelte/internal/index.mjs:196:12)
    at Array.forEach (<anonymous>)
    at run_all (webpack-internal:///../../../node_modules/svelte/internal/index.mjs:202:9)
    at destroy_component (webpack-internal:///../../../node_modules/svelte/internal/index.mjs:1997:9)
    at Object.destroy [as d] (webpack-internal:///./src/components/MyComponent.svelte:885:70)
    at Object.destroy [as d] (webpack-internal:///../../../node_modules/@smui/tooltip/dist/Wrapper.svelte:64:35)
    at Object.destroy [as d] (webpack-internal:///../../../node_modules/@smui/tooltip/dist/Wrapper.svelte:254:40)
    at destroy_component (webpack-internal:///../../../node_modules/svelte/internal/index.mjs:1998:36)
    at Object.destroy [as d] (webpack-internal:///./src/components/MyComponent.svelte:1848:70)

To Reproduce

This is the shortest repro I've been able to get: https://svelte.dev/repl/53b367ea32584baa9139eedcce75641f?version=3.48.0.

Unhelpfully, it generates this callstack:

caught (in promise) DOMException: Failed to execute 'insertBefore' on 'Node': The node before which the new node is to be inserted is not a child of this node.
    at eval (eval at handle_message (about:srcdoc:14:8), <anonymous>:3510:40)
    at run (eval at handle_message (about:srcdoc:14:8), <anonymous>:39:16)
    at Array.forEach (<anonymous>)
    at run_all (eval at handle_message (about:srcdoc:14:8), <anonymous>:45:13)
    at destroy_component (eval at handle_message (about:srcdoc:14:8), <anonymous>:436:13)
    at Object.destroy [as d] (eval at handle_message (about:srcdoc:14:8), <anonymous>:9851:8)
    at Object.destroy [as d] (eval at handle_message (about:srcdoc:14:8), <anonymous>:4181:39)
    at Object.destroy [as d] (eval at handle_message (about:srcdoc:14:8), <anonymous>:4370:44)
    at destroy_component (eval at handle_message (about:srcdoc:14:8), <anonymous>:437:40)
    at Object.destroy [as d] (eval at handle_message (about:srcdoc:14:8), <anonymous>:10033:8)

Which is useless but matches above.

Desktop (please complete the following information):

mwhebert commented 2 years ago

I am also seeing this - It seems to happen for me when I have some elements that were created with tooltips and then I remove some of them from the DOM. The Tooltip onDestroy() - insertBefore is using "nonReactiveLocationStore.nextSibling" which is pointing to the element that was previously removed from the DOM causing insertBefore to crash.

mwhebert commented 2 years ago

FYI: Also wrapping the Wrapper with a div also seemed to resolved the issue. In this case the nonReactiveLocationStore.nextSibling was undefined, or it never got to that line.

tgolsson commented 2 years ago

@mwhebert Did you forget some code snippets in there? I'm guessing it was HTML and got stripped when posting. You might need to use a code block to ensure markdown doesn't to parse it.

    ```html
    your code here
mwhebert commented 2 years ago

@mwhebert Did you forget some code snippets in there? I'm guessing it was HTML and got stripped when posting. You might need to use a code block to ensure markdown doesn't to parse it.

    ```html
    your code here

Thanks - I've updated the comment removing the angle brackets.