svelte icon indicating copy to clipboard operation
svelte copied to clipboard

Svelte 5: Components in #if blocks don't trigger their $effect rune

Open Tagman opened this issue 1 year ago • 3 comments

Describe the bug

I encapsulated a component by an #if block and the component is not rendered, because of a "false" condition. The $effect rune is not triggered even when the data changed. Is this a wanted behavior?

Is there a workaround for this?

Reproduction

If needed, I can create a reproduction but should be straightforward.

Logs

No response

System Info

Arch, Firefox (or any other browser)

Severity

blocking an upgrade

Tagman avatar Jun 28 '24 13:06 Tagman

Can you elaborate on what exactly you are doing or provide a REPL?

Also, the list of $effects dependencies is determined dynamically at runtime — $effect "listens" only for the signals it had read during the previous run.

7nik avatar Jun 28 '24 17:06 7nik

We do need a concrete reproduction for this.

If your question is whether the $effect() block in a component should run when that component isn't even rendered, the answer to that is no. If that isn't the question, we need a repro.

Conduitry avatar Jun 28 '24 18:06 Conduitry

You can hide it using CSS instead which will keep effects active but it will also keep state so you may need to handle that: REPL

If this isn't what you want post a REPL.

henrikvilhelmberglund avatar Jun 29 '24 10:06 henrikvilhelmberglund

We do need a concrete reproduction for this.

If your question is whether the $effect() block in a component should run when that component isn't even rendered, the answer to that is no. If that isn't the question, we need a repro.

Yes, that was basically the question. I assume a {#if false} is not rendering the components inside.

I think I will have to find another way to the rest of my components about the changes.

You can hide it using CSS instead which will keep effects active but it will also keep state so you may need to handle that: REPL

If this isn't what you want post a REPL.

I was thinking along the same lines. But sadly this decreases my performance quite a lot, as I have numerous updates.

Tagman avatar Jul 02 '24 09:07 Tagman

I created a simple REPL to hopefully clear up what I mean. REPL

When the child is not hidden, it logs into the console that the counter was changed. If the child is hidden, the effect is not triggered.

Is there any way I can work around this without "display: none"?

Tagman avatar Jul 02 '24 09:07 Tagman

No - the if block destroys the component, and therefore anything within it. You could use display: none, or you could move the $effect out somewhere else if it's not tied to the component (which it sounds it isn't, if it should run when the component is not visible). Closing as this is nothing we're going to change in Svelte.

dummdidumm avatar Jul 02 '24 09:07 dummdidumm

I updated my REPL . The child now gets passed an array. I added an effect in the App.svelte which references this arr. But this upper most effect is not triggered, even though I added elements to the array. Is there a way to get this effect triggered when an element inside an array is modified?

I understand that the {#if} mechanism isnt going to be changed. I just wanted to know if there is somehow to make this situation work.

Tagman avatar Jul 02 '24 09:07 Tagman

You could use .length as in the child for checking if the length changes. For modifications in the array itself I'm not too sure, I tried .flat() which seems to work but maybe there's something better. REPL

henrikvilhelmberglund avatar Jul 02 '24 11:07 henrikvilhelmberglund

Length would not work in every case, as I typically update an object inside the array. And I would like to have the effect flash then as well.

flat() seems to work, but my next problem lies with the usage if reactive maps. I tried an ugly version with:

        const subservices: Service[] = Array.from(service.subServices.values());
        subservices.flat(Infinity);

But this didn't trigger updates.

Tagman avatar Jul 02 '24 14:07 Tagman