svelte
svelte copied to clipboard
Check Svelte component instance is detached or not from the document after a microtask tick.
Describe the bug
Currently we dealing with incorporating of Svelte component in out big application and at this point I've noticed that Svelte mount several components under shadow dom. Looking at this issue with duplicate mounts I've tracked to
https://github.com/sveltejs/svelte/blob/1366932dc0d3d519f9f79ccfe50c0c3190a6f809/packages/svelte/src/internal/client/custom-element.js#L92
async connectedCallback() {
this.$$cn = true;
if (!this.$$c) {
// We wait one tick to let possible child slot elements be created/mounted
await 0;
if (!this.$$cn) {
return;
}
....
here we check this.$$cn and this.$$c but after a tick we refer to this.$$cn only
I've slightly modified this part to recheck both this.$$c and this.$$cn after a tick like this
async connectedCallback() {
this.$$cn = true;
if (!this.$$c) {
// We wait one tick to let possible child slot elements be created/mounted
await 0;
if (!this.$$cn || this.$$c) {
return;
}
and not I don't see any duplicate components.
My strong believe is that we should check both this.$$c and this.$$cn after a tick and seems like we have an issue here?
Reproduction
no exact steps. Issue is observable with complex application where we have triggered many mounts/unmounts of the component
Logs
No response
System Info
System:
OS: macOS 14.3
CPU: (8) arm64 Apple M1
Memory: 59.86 MB / 16.00 GB
Shell: 5.9 - /bin/zsh
Binaries:
Node: 18.17.1 - /usr/local/bin/node
Yarn: 3.6.1 - /opt/homebrew/bin/yarn
npm: 9.6.7 - /usr/local/bin/npm
npmPackages:
rollup: 2.78.0 => 2.78.0
svelte: 4.2.8 => 4.2.8
Severity
annoyance
Makes sense, PR welcome!
Some more context and steps to reproduce: The issue appears, when moving/removing the element directly after it has been attached to the DOM.
- create a new svelte project via
npm create vite@latest
-> select svelte - in svelte.config.js set
customElement: true
under compilerOptions - in Counter.svelte add
<svelte:options customElement="my-element" />
at the beginning of the file - run the app (npm i && npm run dev) and open it in your browser
- open dev tools JS console and execute
var element = document.createElement('my-element'); document.body.appendChild(element); element.remove();
and see errorUncaught (in promise) TypeError: Cannot read properties of undefined (reading '$destroy') at Component.js:339:15
- execute
var element = document.createElement('my-element'); document.body.appendChild(element); element.remove();document.body.appendChild(element);
and you can see twobutton
tags inside the shadow dom
Misbehaviour in both cases doesn't occur if you wait a tick after first DOM attachment:
-
var element = document.createElement('my-element'); document.body.appendChild(element); await 0; element.remove();
-
var element = document.createElement('my-element'); document.body.appendChild(element); await 0; element.remove();document.body.appendChild(element);