svelte
svelte copied to clipboard
Don't destroy components inside falsey {#if} blocks
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
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.
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.
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.
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.
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...
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.
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.
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.
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.
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.
How about using display none and transition-behavior to enable animations ? The solution is described in this article.
@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...
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;
}
@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.