-
-
Notifications
You must be signed in to change notification settings - Fork 4.6k
Description
Current state
In Svelte 4 (and Svelte 5 without runes mode), it is very easy to unidirectionally send data from a child to a parent, both static/dynamic data, using props/constants:
<!-- Parent -->
<script>
let value
</script>
<Child bind:value />
{value}
<!-- Child -->
<script>
export let value = 123
// or
export const value = 123
</script>
<!-- on click: value++ -->
The problem
Trying to do the same thing with runes activated in the Child component doesn't work. Trying to bind to an export const
fails with the error bind_invalid_export
, which then explains it's disallowed to bind to a const
. Trying to bind to a $bindable
prop instead fails with the error:
props_invalid_value
Cannot do `bind:value={undefined}` when `value` has a fallback value
So, it's become impossible to easily send a value from a child to a parent.
Alternative
Use bind:this
, which is harder to use, especially with dynamic values, and more verbose/uglier when you need just one value:
<!-- Parent -->
<script>
let child
</script>
<Child bind:this={child} />
{child?.staticValue}
{child?.dynamicValue?.value}
<!-- Child -->
<script>
export const staticValue = 100
export const dynamicValue = $state({ value: 123 })
</script>
<!-- on click: dynamicValue.value++ -->
Use case
My current use case is something like:
<script>
let instances = []
</script>
{#each someArray as value, i}
<Child {value} bind:instance={instances[i]} />
{/each}
The child component has an instance of a third-party library class that attaches to a DOM element, like so:
<script>
export const instance = new LibraryClass(...)
let elem
onMount(() => {
instance.init(elem)
return () => instance.destroy()
})
</script>
<div bind:this={elem}></div>
Proposed solution
Either re-allow the previous behaviour of binding to constants or to props with a default without passing a value, or introduce a directive that is specifically for passing data from a child to a parent, like get:childValue={value}
. It should be documented that any changes to value
will not be passed to the child and will be overwritten whenever the child emits a new value, and maybe the compiler can even emit errors/warnings when value
is mutated directly, but I think it can get difficult to do so (e.g. get:childValue={some.deep[0].state}
). The get:
solution I think makes more sense since bind
is supposed to be bi-directional.
Severity
annoyance