Skip to content
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

Don't destroy components inside falsey {#if} blocks #9976

Closed
PeytonHanel opened this issue Dec 20, 2023 · 14 comments
Closed

Don't destroy components inside falsey {#if} blocks #9976

PeytonHanel opened this issue Dec 20, 2023 · 14 comments

Comments

@PeytonHanel
Copy link

Describe the problem

When using conditional blocks (e.g.: {#if}, {:else}, etc), any component on the inside will be destroyed/unmounted when the condition becomes false. This makes it difficult to persist state within conditional blocks, as all logic must be moved to the parent.

Describe the proposed solution

Add some kind of an option to cause a conditional block to not unmount/destroy the markdown/component inside of itself. For example, something to the following effect could be done:

{#if condition, persist?}

{:else}

{/if}

Alternatives considered

Open to anything.

Importance

would make my life easier

@Conduitry
Copy link
Member

Does a conditional style='display: none' work for you? I don't think we want to add new API for this or make it so that every component has to account for the fact that it might not have any corresponding DOM elements just in case someone happens to include it in the type of #if block you're proposing.

@PeytonHanel
Copy link
Author

It doesn't work in every situation I've needed this. style='display: none' is less performant than {#if} and it also doesn't let me use certain features included with svelte, such as svelte/transition. I prefer to use the {#if} block always but if I have to choose between the two then the code has more opportunities to get messy.

@CallMeLaNN
Copy link

It doesn't work in every situation I've needed this.

I recommend thinking what it supposed to be instead. Conditional block meant to be as it is, same with anywhere else. Everyone expecting it to be destroyed since the element is not there, in the DOM tree.

The doc said

A transition is triggered by an element entering or leaving the DOM as a result of a state change.

Svelte transition is for that and definitely coupled with mount/destroy.

So you should fix your design. Either use css transition for the show/hide or rearrange your logic. Btw complex logic better to be placed in a store instead of in a component.

@adiguba
Copy link
Contributor

adiguba commented Dec 22, 2023

display: none is the simplest solution as long as you don't need animation, and I don't see any performance issue with it :

<div style:display={visible ? null : 'none'}>
	Lorem ipsum
</div>

But this do no allow transition...

In order to do this, you should use an action, but unfortunately, currently there is no way to use the transition module manually (see issue #9911).
So you have to manage transitions with others tools...

@gregg-cbs
Copy link

In React we used stores too - we also use localStorage to hydrate stores on page refresh for more persistent data. We expect to do the same in Svelte as recommended by @CallMeLaNN .

Porting my Nextjs app to Svelte and I must say, Svelte is soooooo much better! Thanks svelte team.

@PeytonHanel
Copy link
Author

But this do no allow transition...

@adiguba I'm using transitions so that's one reason why I don't use display: none

In React we used stores too - we also use localStorage to hydrate stores on page refresh for more persistent data. We expect to do the same in Svelte as recommended by @CallMeLaNN .

Porting my Nextjs app to Svelte and I must say, Svelte is soooooo much better! Thanks svelte team.

@gregg-cbs Thanks for the recommendation I'll look into this.

@adiguba
Copy link
Contributor

adiguba commented Dec 27, 2023

I tried to implement a solution to use transitions without creating/destroying elements (so without {#if} or {#each}), and I make a small functional PoC using transition:this.

I choose transition:this because it's currently invalid in Svelte, even if it's not necessarily the best choice...
I also thought about using style:display={visible} directly, but that could cause compatibility problems

Anyway, transition:this={visible} could mean that we want to display (or hide) the current element depending on the value of visible.
Basically when visible is a falsy value, the DOM element will be hidden using a display: none !important... after executing element transitions.

Something like this

<div transition:slide transition:this={visible}>
  ...
</div>

The PoC can be tested here : https://adiguba-svelte-5-test-git-transition-this-adigubas-projects.vercel.app/#H4sIAAAAAAAACpVTTY_TMBD9K6MU0a3YNt0ucEjTSAgkLtxYcaEcnHjSWuvYlj3pbhXy37GddFt2D7CKosQzb958-XVJLSS6JPvZJYo1mGTJJ2OS64SOJhzcASWhPzvd2ipYcldZYajYqi2JxmhL0IGTguN1LY_QQ211A9MhMCXLlBMktJquLyI-68aMwEUaDosBH0EeVmnlCHhrWQiFDbxfLWO8RIKDcKKU6K1vHDHCK7Itzrw7T8-1qVyyEmWRC2VagtDOZlrtsbov9eMUSqF4Fo_IN93I2BfwY_jL0yE6EnFxgEoy5zbbZGcF3yax-W4i6lMtfTBcAKcxybn5LA7o907qkslN15066_tItaW7vXBQSl2B_3LhjGRH5PAgaD8AcmOxeDu5Wd2uL_IGw4d1ngbniIujTeMpT31FQ62pqHsYZvvPMv-q79JLvsiLYb2m8ucsQyev6uKpm7ATR0c5gOOioBvixvxZLOh-PRgfBKd9BrWgub9XhIpGR8PsTqgMbrAB1pIeza1DO3cosaIMlFYY7XHF4fVVLMI1eJEzGEeK8DsnbLyD0GeVbaOcT1Tb8J5AzGRwuzKPZ_7A7Xdyoi615Wh9nHkEp_1uYLJcLsdwwzgXaue9H0cKH8Cq-53Vrb_cMKlvwnMi9-KIM_NabjQXtUCeZEE5_fWT9MPU_1f7g0S9oIMQFT7AlyDF2YL0N10xiXfe852sr_BqdqFqLXEh9e4qBr6DbQJZjI4Lryx6Ei-w2XMxO8NU8RUV2gAARpCXRRdI-jwtCw8OgAB92d-v_g9CGD3W5gQAAA==

It's still a little buggy (especially when interrupting a transition) and incomplete (need some compile-time verification, SSR support).
But I think it gives you an idea of what it could do.

@CallMeLaNN
Copy link

Svelte transition is a limited and convenient way to apply transition when element enter or leave DOM.

Like I said it simply achievable using CSS transition. I'm not sure why this is not suitable https://svelte.dev/repl/5f43b715812845c3ad153cab01c4d234?version=4.2.8
It's simple enough and I just did it from mobile.

<div class="fade" style:opacity={visible ? "1" : "0"} >
	Lorem ipsum
</div>

<style>
	.fade {
		transition: opacity 1s;
	}
</style>

You can wrap with a component or use Svelte action to make it less.

@adiguba
Copy link
Contributor

adiguba commented Dec 28, 2023

Like I said it simply achievable using CSS transition

And Svelte's transition are achievable using CSS animation, but they allow use to correctly handle all the process (like destroying the element after the animation end, creating dynamic animation, etc).

Your example is very simple and incomplete, as the DOM element is not marked as "display:none" it's still present on the DOM.
He still occupies his place and react to pointer-events...

Another example that can be more difficult in pure CSS : slide
=> You cannot make a transition from height: auto, and you have to set the real size explicitly before running the transition...

All these things are already managed by Svelte's transitions... except that it destroy the element instead of hide it.

@n0n3br
Copy link
Contributor

n0n3br commented Jan 26, 2024

How about using display none and transition-behavior to enable animations ?
The solution is described in this article.

@adiguba
Copy link
Contributor

adiguba commented Jan 27, 2024

@n0n3br : transition-behavior is still experimental and not supported by all browser...

And you have to create a transition using CSS.
You cannot reuse Svelte's transitions...

@n0n3br
Copy link
Contributor

n0n3br commented Jan 27, 2024

Chrome, Edge and Opera already support it. That represents maybe 65 to 66 percent of the market. Other browsers will come in the future. Maybe we could think of already integrating this option in some of the transitions svelte provides. MDN documentation states that to keep compatibility, all you have to do is to use a transition without this option before the one that includes it.

.card {
  transition: all 0.25s;
  transition: all 0.25s allow-discrete;
}

.card.fade-out {
  opacity: 0;
  display: none;
}

@pooledge
Copy link

@adiguba this is as close as I could get for the slide CSS equivalent. My final component accepts a prop to fallback to a svelte's slide transition if I do not want to (or may not) keep elements in the DOM.

@dummdidumm
Copy link
Member

Closing because solving this through a controlled display: none is the right way. The fact that transitions should run (or not run) based on that is a separate issue that I believe is tracked somewhere.

@dummdidumm dummdidumm closed this as not planned Won't fix, can't repro, duplicate, stale Nov 4, 2024
@adiguba adiguba mentioned this issue Dec 21, 2024
6 tasks
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

8 participants