-
-
Notifications
You must be signed in to change notification settings - Fork 4.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[Svelte 5] Mutating non-bound props lead to undefined behavior ($binds
rune proposal)
#9997
Comments
$binds
run proposal)
Regarding #9995, this proposal would make the solution explicit but not very elegant // Parent
<Middle bind:ref color="red" />
// Middle component
<script>
const {...props} = $props()
let {ref} = $binds()
</script>
<Button bind:ref {...props} />
// Button
<script>
const {color} = $props()
let {ref} = $binds()
</script>
<div bind:this={ref} style:color /> |
This is very bad... You need to understand why. In Svelte, a parent bind:value can do 2 things: With this, you have 4 constellations: 1) What, if I don't like to listen or control prop? 2) What, if only want to control child prop from parent prop? 3) What, if only want to "listen" (read only) child prop from parent prop? 4) What, if I need to listen and to control? And... here is a problem today with Svelte 5 bind:value and default prop: |
$binds
run proposal)$binds
rune proposal)
Thanks for your input @dm-de, I'm glad you're taking the time to make a detailed response, although I'm not sure to understand everything. I honestly know that the Anyway, Rich Harris made a long stance on props mutability reactivity and potential bugs in #9739.
There is, at this time of writing (next.26), a discrepancy between scalars (this is the bug described in the first message) and objects. While objects are made readonly thanks to proxying, scalars are mutable but somehow not reactive. The documentation reads this inaccurate statement:
This is only true for objects, not scalars. You made 4 great demos, but some of them are problematic:
I'm very divided regarding 2. I don't like the idea of having no way for a component to tell its parent that a value can be mutated and needs to be bound: <script>
// There is no way for a component to enforce `bind:` on the parent
let { iWillChange, iWont } = $props()
// Updating iWillChange will never be an issue, but updating iWillChange.key
// will throw if the parent did not bind the prop
</script> Proposal: <script>
let { iWillChange } = $binds()
const { iWont } = $props()
// No ambiguity at all!
</script> What if you want to provide a value but don't want to keep track of it? Well, you'll be forced to <script>
// In +page.svelte
const {data} = $props()
// Make an editable copy
let name = $state(data.name)
</script>
<!-- <input {value} /> wouldn't be possible anymore, I'm pretty sure no maintainers will allow it -->
<input bind:value={name} /> It might also be worth enforcing I guess I want This would make a nice symmetry:
|
I don't fully understood this before all the time. I think in Svelte 5, this are not two states, this is ONE SINGLE state shared between parent and child: PARENT:
CHILD:
So, if i change child value, i change parent value at same time without data transfer. If all this is correct, then I can not bind derived value (it is immutable). If $props accept all types of props (bind or not),
Here is only one question: Parent: Child: where foo is mutable And what, if I use child differently at same time:
Something bad can happen! Because Child expect a mutable "foo". So... We have 2 options to fix that problem!
Personally, i prefer solution 1 - current Svelte system. |
Not really, IIRC Svelte 5 uses signals, which behave roughly the same as the <script>
import {writable} from 'svelte/store'
let value = writable(123) // $state() gets compiled to writable()
</script>
<Child bind:value /> This means that there is indeed data transfer between the parent and the child, made completely invisible by the compiler.
This is not really related to <script>
let name = $state('foo')
let double = $derived(name + name)
</script>
<input bind:value={double} /> <!-- Can't do this, double is immutable -->
100%, that's why I'd like a way for a component to announce which props it might mutate
Maybe more!
I completely get why you prefer option 1, but I shot my foot today because of how things are implemented |
Closing in favor of #10768 - it will describe what is bindable, though not what is required to be bound; that one we probably won't add. |
Describe the bug
Given a parent:
And a child that can mutate its
value
prop:Updating the value in the child will skip $inspect but will properly update
value
(see reproduction)The documentation reads
The behavior described above is not raising an error during development. However, I believe this should be enforced at compile time. Mutations have always created undefined behaviors in Svelte (mutations are always possible but not reactive) and it should be possible to solve it in Svelte 5 by forcing either immutability or bindings:
Writing
<Child {value} {size} />
would cause a "value must be bound" errorIn the case of objects, I guess we could step a bit away from usual js where props are always mutable:
Reproduction
https://svelte-5-preview.vercel.app/#H4sIAAAAAAAACo2QT2uEMBDFv8o0LKjgn3tQYdtTocfemh7cOFsDMQlJtCzid2-M7Zaye-glMHlv3sz8FnIWEh2hbwtR3YiEkqMxJCf-YrbCzSg9htrpyfLtp3bcCuNbppgXo9HWw7Myk4ez1SMkZRWrcm9MNpdED3MnJ4QGDs53HtMkyZiqq98oVZv2RfNOQjRQqEUMPQnV0725auvKtNH5NAjZB88-eIn6ejUEy0NRMPU6CAefQko44dDNCFxbi9zLC40pj3pSPfA_WXfmFUV4AoFR9-IssCfU2zAuvwKLnf9FtsH4WTjgMFYbl2abchDKmbBeGtUbPjc8tIpfzZJm0LThOOW0xFLqj5SRb5GRfEefrdXdK97XL6KIi9ECAgAA
Logs
No response
System Info
Severity
annoyance
Edit: merry Christmas! 🎅
The text was updated successfully, but these errors were encountered: