Given this Svelte 4 setup REPL
<script>
import Comp from './Comp.svelte'
import {writable, derived, get} from 'svelte/store'
function newCustomStore() {
const store = writable({1: 'foo'})
return {
subscribe: store.subscribe,
set: store.set,
getEntry(id) {
return get(store)[id]
}
}
}
function newDeviceStore(deviceMap, id) {
return derived(deviceMap, $deviceMap => {
return deviceMap.getEntry(id)
})
}
const store = newCustomStore()
const entryStore = newDeviceStore(store, 1)
function handleOpenComp() {
const comp = new Comp({
target: document.body,
props:{
entry: entryStore
}
})
}
</script>
<!-- not reactive -->
{store.getEntry(1)} <br>
<!-- reactive -->
{$entryStore} <br>
<button on:click={handleOpenComp}>
open comp
</button>
<button on:click={() => $store[1]= $store[1] + 'o'}>
click
</button> <br>
<script>
export let entry
</script>
<!-- reactive -->
{$entry}
It works to pass a derived store to the component that is reactive to changes of the store it's derived from.
When I transform this to Svelte 5 REPL
<script>
import { mount } from 'svelte';
import Comp from './Comp.svelte'
import {writable} from 'svelte/store'
let target
class Store {
value = $state({1: 'foo'})
getEntry(id) {
return this.value[id]
}
}
let store = new Store()
let props = $derived({ entry: store.getEntry(1)})
function handleOpenComp() {
const comp = mount(Comp, {
target: document.body,
props: props
})
}
</script>
<!-- reactive -->
{store.getEntry(1)} <br>
<!-- reactive -->
{props.entry} <br>
<button onclick={handleOpenComp}>
open comp
</button>
<button onclick={() => store.value[1] = store.value[1] + 'o'}>
click
</button>
<br>
<script>
let {entry} = $props()
</script>
<!-- not reactive -->
{entry}
the $derived props won't react to changes. The docs say
"...use $state instead to create a reactive property object and manipulate it."
const props = $state({ foo: 'bar' });
const app = mount(App, { target: document.getElementById("app"), props });
props.foo = 'baz';
I seem to be wrong to assume, that when passing $state should work, passing $derived should do so as well...? How can I achieve the same behaviour in Svelte 5?
$derived
doesn't reactifies the value, the variable itself is reactive.
For the value, you create an object and populate it with a primitive value ("foo"
), so there is nothing reactive.
The variable is reactive only within its scope. Thus, when you pass props
, the value that is non-reactive from the beginning goes beyond the props
scope.
You can use closure to keep reactivity:
props: {
get entry() { return props.entry }
}
Reading entry
in the child component will cause reading props
, and thus reactivity will be passed.