Skip to content

Svelte 5: value in template becomes unreactive under seemingly random circumstances #11012

Closed as not planned
@Evertt

Description

@Evertt

Describe the bug

Well, the best way I can describe the bug is by showing you. This will take at most 3 minutes of your time.

https://youtu.be/4JdoGHOIY4k

In short, since I know that Svelte uses a stringify() function in its render_effect() functions for its templates, which looks like this:

function stringify(value) {
    return typeof value === 'string' ? value : value == null ? '' : value + '';
}

I know that if I have anything that's not a string (e.g. an object), but that can be casted to a string (e.g. if it has a toString() method), then it can be used in Svelte 5 templates plainly and Svelte's stringify() function will make sure it's casted to a string. I'm experimenting with how this can enable me to create objects and / or functions that "just work" in templates. So far it always worked well, but this time it stops working, but only under very specific circumstances. Watch the video to see what I mean.

Reproduction

Here is the REPL, so you can play around with it.

But I really recommend you watch the video to see when things go wrong in the template and stop being reactive.

Logs

No errors, just the template not updating under specific (but seemingly random) circumstances.

System Info

Using the REPL which uses Svelte v5.0.0-next.90.

My browser is Arc:
Version 1.36.0 (48035)
Chromium Engine Version 123.0.6312.87

Severity

annoyance

Edit

I now see that depending on what line I uncomment / active, the generated output code changes dramatically.

When my increment function looks like this:

const increment = () => {
    count.value = count + 1
}

Then the generated output code looks like this:

const increment = () => {
    $.mutate(count, $.get(count).value = $.get(count) + 1);
};

// And then a little later
$.render_effect(() => $.set_text(text, `count is ${$.stringify($.get(count)())}`));

$.render_effect(() => {
    $.set_text(text_1, `count is ${$.stringify($.get(count).value)}`);
    $.set_text(text_2, `count is ${$.stringify($.get(count))}`);
});

But when my increment function looks like this:

const increment = () => {
    count(count + 1)
}

Then the generated output code looks like this:

const increment = () => {
    count(count + 1);
};

// And a little later
$.render_effect(() => $.set_text(text, `count is ${$.stringify(count())}`));
text_2.nodeValue = `count is ${$.stringify(count)}`;
$.render_effect(() => $.set_text(text_1, `count is ${$.stringify(count.value)}`));

Notice how suddenly Svelte doesn't bother generating $.mutate() or $.get() code in the increment function anymore? Even though that shouldn't even be necessary anyway... And notice how text_2 has been taken out of the render_effect()? That's clearly why it stopped being reactive. But why Svelte would make these choices is beyond me...

Edit 2

The only thing I can guess, is that for some reason Svelte concludes that when I use count(count + 1) (or count(count() + 1) I've also tried), that the plain count cannot by reactive. Probably because it thinks that functions cannot be reactive. But since functions can in fact be adapted to have a [Symbol.toPrimitive]() method attached to them and therefor can be casted to a string which could reactively call some kind of inner state, I would recommend that Svelte stops assuming functions cannot be reactive.

Because also, the cost of being wrong in that scenario, is negligible. If you indeed create a render_effect() like so:

$.render_effect(() => $.set_text(text_2, `count is ${$.stringify(count)}`));

And it turns out that count is indeed a plain function that produces no reactive output when it's casted to a string, then you've lost nothing, because then that render_effect() will never run a second time...

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions