Svelte: Event "destroy" not exist in Svelte3 anymore.

Created on 24 Jun 2019  Â·  16Comments  Â·  Source: sveltejs/svelte

I found one of important lack of functionality during porting my code to svelte3 from the second version. Looks like we have no "destroy" event outside of component anymore. It's an important part of the public component API.
Basically, we have $on the method but without the "destroy" event it's useless
Also, we should have some extra lifecycle hooks outside of the component, it's important for routing and some other cases.

All 16 comments

The difference in Svelte 3 is that component's can't self-destruct — there's no equivalent of this.destroy(). If a component can't destroy itself, that implies that some parent or controller must destroy it, which means you already have a place to put whatever logic you were going to put in the event handler.

Inside a component, you can use onDestroy (or a function returned from onMount) to handle any internal cleanup.

What's a (concrete) scenario in which this is necessary?

Current workaround with a one-liner (+import) for each component that needs an observable destroy event (using public api):

https://svelte.dev/repl/689a66f5a7af4f498a7d4519a63c49af?version=3.5.4

// Comp.svelte
useDestroyEvent();

// other.js
export const useDestroyEvent = () => {
    const dispatch = createEventDispatcher();
    onDestroy(() => dispatch('destroy'))
}

comp.$$.on_destroy.push( ()=> console.log('asda'))
its work

svelte-state-renderer now has a PR to simply call the appropriate code upon destroying the component - https://github.com/TehShrike/svelte-state-renderer/pull/21

As I understand it, this ASR bug was why this issue was opened in the first place. Is there another reason to have this?

.$$.on_destroy.push is not a viable long-term solution, as nothing in $$ is guaranteed to stay the same between versions of Svelte.

component.$destroy() is it atomic operation or it's the async method?

Current workaround with a one-liner (+import) for each component that needs an observable destroy event (using public api):

https://svelte.dev/repl/689a66f5a7af4f498a7d4519a63c49af?version=3.5.4

// Comp.svelte
useDestroyEvent();

// other.js
export const useDestroyEvent = () => {
  const dispatch = createEventDispatcher();
  onDestroy(() => dispatch('destroy'))
}

@Rich-Harris I need something like this, but if I run onDestroy outside I get next error:

Uncaught Error: Function called outside component initialization

probably because I am using splitting code and this js contains in the separate chunk.

hmm for me onDestroy not working at all.

Sorry, it's happening because it was in my rollup config:

extensions: ['.js', '.mjs', '.json', '.html'],

but in your store.mjs you make local import (from '../internal') in that case rollup didn't check package.json and firstly try index.js after start working commonjs plugin and dublicate set_current_component method.
I fix it just change order for extensions:

extensions: ['.mjs', '.js', '.json', '.html'],

Closing this. As Rich mentioned, the only way a component can be destroyed now is if someone destroys it. Re-open if you have a specific case where this event is necessary and you can't just emit it yourself.

Hey @Conduitry. It crossed my mind that there might be a use case for this, relating to third party libraries.

It's quite common to use a third party library to do something for you in the DOM. For instance, Choices.js or Material Datetime Picker. It's also quite common to wrap the functionality of those libraries in your own generic components, for use in a lot of places, where the consumers don't need or want to worry about the complexity within those components, particularly that concerning the interaction with the third party library.

In such cases, it can often be important to handle the generic component being destroyed. Otherwise, the library might malfunction or exhibit a memory leak, unaware that its elements have been removed from the DOM. If it is possible to listen for a destroy event, the library can be handled automatically by the generic component when that event fires, without any work on the part of consumer of the generic component.

Such manual handling would seem to be problematic in several regards. First, the handling could be quite complex, depending upon the nature of the functionality of the generic component. It might be thought that consumers should not be exposed to such complexity. Second, it would seem to constitute unpleasant boilerplate, required for each and every instance in which the generic component is used. Third, it would seem error-prone, as a consumer could easily forget to implement the boilerplate.

I've used destroy in Svelte 2 for this purpose on several occasions. I would imagine that similar occasions could arise for users of Svelte 3, and thus a destroy event would seem to be of value.

@ChrisTalman In the case of wrapping components, I tend to use the onMount return value (onDestroy). This will be called if Svelte removes third party componenents from the DOM at any point.

import { onMount } from 'svelte'
import SomeComponent from 'some-thirdparty-js'

let thirdParty

onMount(() => {
 thirdParty = new SomeComponent()

 return () => thirdParty.destroyMethod()
})

@antony Nice, thanks. So, that is a destroy event, just with an extra step?

@ChrisTalman destroy event needs outside the component tree. To understand more you need to read this issue #779

@stalkerg can you explain the scenario where you need destroy outside the component tree more clearly please?

I've migrated 30 + components and 2 of 3 apps to Svelte v3 from v2 and I've not yet encountered this issue. I'd like to understand your use case (ideally with a REPL).

Hi @Conduitry
I found a similar issue inside the storybook version for Svelte.

Storybook addons use decorators to wrap the user story inside some wrapper component.
This is done just here in the code.

That code still contains legacy code for svelte 2 that does not work anymore:

    component.$on('destroy', () => {
      wrapper.$destroy(true);
    });

The problem is that the onDestroy callback is never called inside a decorator (the wrapper component).
I've created an issue on the storybook repository for that.

I've tryed the solution proposed by @timeshift92 by replacing the above 3 lines of code with component.$$.on_destroy.push(() => wrapper.$destroy()); but then I have a warning in the console saying index.js:47 TypeError: Cannot read property 'removeChild' of null.

My knowledge on Svelte is not sufficient here to solve the problem.
Still I am wondering if the components creation is correct in that case...

@antony look at @tonai example. Basically, as Rich said "the only way a component can be destroyed now is if someone destroys it" the main problem when you can't control such destroys and it's happening not in your code.
For me it was the ASR render plugin for @tonai it's the storybook decorator.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

Rich-Harris picture Rich-Harris  Â·  3Comments

juniorsd picture juniorsd  Â·  3Comments

mmjmanders picture mmjmanders  Â·  3Comments

thoughtspile picture thoughtspile  Â·  3Comments

matt3224 picture matt3224  Â·  3Comments