svelte
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
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
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==
Even worst, it's actually every "complex" prop like objects or array. It's treated as immutable.
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.
Behavior in Svelte 4
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.
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>
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.