Skip to content

Event "destroy" not exist in Svelte3 anymore. #3089

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
stalkerg opened this issue Jun 24, 2019 · 17 comments
Closed

Event "destroy" not exist in Svelte3 anymore. #3089

stalkerg opened this issue Jun 24, 2019 · 17 comments

Comments

@stalkerg
Copy link
Contributor

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.

TehShrike added a commit to TehShrike/svelte-state-renderer that referenced this issue Jun 24, 2019
A fix for #19

Hopefully temporary, until sveltejs/svelte#3089 can be resolved in a more satisfying way.
TehShrike added a commit to TehShrike/svelte-state-renderer that referenced this issue Jun 24, 2019
A fix for #19

Hopefully temporary, until sveltejs/svelte#3089 can be resolved in a more satisfying way.
@Rich-Harris
Copy link
Member

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?

@ryanatkn
Copy link
Contributor

ryanatkn commented Jun 24, 2019

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'))
}

@timeshift92
Copy link

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

@Conduitry
Copy link
Member

svelte-state-renderer now has a PR to simply call the appropriate code upon destroying the component - TehShrike/svelte-state-renderer#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.

@stalkerg
Copy link
Contributor Author

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

@stalkerg
Copy link
Contributor Author

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.

@stalkerg
Copy link
Contributor Author

hmm for me onDestroy not working at all.

@stalkerg
Copy link
Contributor Author

stalkerg commented Jun 25, 2019

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'],

@Conduitry
Copy link
Member

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.

@ChrisTalman
Copy link

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.

@antony
Copy link
Member

antony commented Aug 20, 2019

@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()
})

@ChrisTalman
Copy link

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

@stalkerg
Copy link
Contributor Author

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

@antony
Copy link
Member

antony commented Aug 22, 2019

@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).

@tonai
Copy link

tonai commented Mar 3, 2020

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...

@stalkerg
Copy link
Contributor Author

stalkerg commented Apr 2, 2020

@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.

@blairn
Copy link

blairn commented Feb 11, 2021

I can tell you, sometimes it would be a wonderful thing if we could make sure all slots in a thing had their on Destroy called before the parent one was called.

I'm looking at you mapbox-gl.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

9 participants