svelte icon indicating copy to clipboard operation
svelte copied to clipboard

A bindable array-type prop that uses a non-array prop to build its default value results in non-reactivity for the bindable property

Open kran6a opened this issue 9 months ago • 7 comments

Describe the bug

let {min = 0, max = 100, value = $bindable([min])} = $props();

Results in value not being reactive

Reproduction

https://svelte-5-preview.vercel.app/#H4sIAAAAAAAACkWOQarDMAxEryJEFykY6m7z40LPEWfhJmoRxIqxndIScvePsulu9AbNzIZPnqlg228oIRK2eE8JDdZv0qO8aa6EBsuy5lFJV8bMqd68-DpThS2ygANrIIYPOLhaa-Ad5pXAwenBMoXHTE0fWYbzrizlJZXm_KcJJ5aSaKzN8aGsu_wKpEtHz3a4vR129ZV1LGmtoCudxxzkRR4hsjiPVlX4OI9Xq1o3tEeC-wVdbmgwLhM_mSZsa15pH_Z_rDIG3w8BAAA=

Logs

No response

System Info

Does not matter

Severity

annoyance

kran6a avatar May 02 '24 09:05 kran6a

The problem is not even $bindable is just the array itself

https://svelte-5-preview.vercel.app/#H4sIAAAAAAAAE0WOwQrCMAyGXyUEDwoF63Wugs-x7lC3KIE1K20nyti7S3bx9uf7k4-s-OSJCjbdihIiYYP3lNBg_SYdypumSmiwzEselLRlyJzqzYuvE1VYIws4sAZi-ICDi7UG3mFaCBx0kaXfwMEh5TmV4-mqZweWkmiox31NWXv-W6VNu3zd2872m_bKWpa0VNDXnMcc5EUeIbI4j1ZT-DiPF6v5wTI2u8H9RecbGozzyE-mEZuaF9r67QfcwWdwBAEAAA==

paoloricciuti avatar May 02 '24 12:05 paoloricciuti

Even worst, it's actually every "complex" prop like objects or array. It's treated as immutable.

paoloricciuti avatar May 02 '24 12:05 paoloricciuti

I'll try to investigate a bit further later but in the meantime i'll send my findings:

by being immutable by default in runes mode mutating prop.something or prop[something] is not recognized as a "reassignment". The problem is there even for legacy mode with immutable option set to true via svelte:options. However this is by design so i'm not sure this is actually a bug.

The fact that svelte treat this as immutable also mean that trying to modify a prop deeply will not work because the equality fn will just involve the top level.

paoloricciuti avatar May 02 '24 14:05 paoloricciuti

Behavior in Svelte 4

paoloricciuti avatar May 02 '24 14:05 paoloricciuti

If you're not using the fallback value and instead pass something bound in, the value is reactive - playground example. So I think maybe we should proxify the default value? On the other hand, if you pass in $state.frozen it won't work aswell, and we can't really know if the property is intended to be used with $state or $state.frozen. This is an edge case with no clear right answer.

dummdidumm avatar May 02 '24 14:05 dummdidumm

If you're not using the fallback value and instead pass something bound in, the value is reactive - playground example. So I think maybe we should proxify the default value? On the other hand, if you pass in $state.frozen it won't work aswell, and we can't really know if the property is intended to be used with $state or $state.frozen. This is an edge case with no clear right answer.

But also it's a byproduct of every prop being immutable in svelte 5. And if you use immutable in svelte 4 the behavior is the same. I can't remember the reason why immutable={true} was chosen as the only option but this would kinda negate that.

On the other hand it does seems a bit of a smell. What if you could also use $state and $state.frozen for the fallback value?

<script>
    let { value: $state([0]), other_value: $state.frozen([0]), min: 0 } = $props();
</script>

paoloricciuti avatar May 02 '24 16:05 paoloricciuti

On discord another example was brought up where it's completely unexpected that a prop is frozen.


if you pass in $state.frozen it won't work aswell, and we can't really know if the property is intended to be used with $state or $state.frozen.

You can pass a non-reactive object and reactivity won't work either.

Knowing that props are reactive, I'd expect the fallback value also to be reactive in a regular way ($state). Otherwise, it must be documented, and a way to change this behaviour must be provided.

7nik avatar May 06 '24 21:05 7nik