This is how I used to do it in Svelte 4:
// $lib/firebase.tsexport function writableRealtimeStore<T>() { let unsubscribe: () => void = () => {} let objectRef: any const store = writable<T | null>(null) let storeSet = store.set return { subscribe: store.subscribe, set: (value: any) => { return set(objectRef, value) }, update: () => {}, setPath: (path: string) => { objectRef = ref(realtimeDB, path) unsubscribe() unsubscribe = onValue(objectRef, (snapshot) => { storeSet((snapshot.val() as T) ?? null) }) }, }}// $lib/stores.tsexport const myStore = writableRealtimeStore()// routes/+page.svelte<script lang="ts"> import { myStore } from '$lib/stores' myStore.setPath('/books/<book_id>')</script><input type="text" bind:value={myStore.bookName}
This store is reactive both ways - when the value in the DB changes, it updates the UI, and when the user updates the value of the input, the DB changes. I could access the properties of my DB object directly as myStore.bookName
.
However with Svelte 5 I can't get the same behavior of the store object:
// $lib/firebase.tsexport function createRealtimeStore<T>() { let unsubscribe = () => {} let store: { value: T | undefined } = $state({ value: undefined }) let _ref: DatabaseReference return { get value(): T | undefined { return store.value }, update: () => { if (_ref) set(_ref, store.value) }, setPath: (path: string) => { _ref = ref(realtime, path) unsubscribe() unsubscribe = onValue(_ref, (snapshot) => { store.value = snapshot.val() }) }, unsubscribe, }}// $lib/stores.tsexport let myStore = createRealtimeStore()// routes/+page.svelte<script lang="ts"> import { myStore } from '$lib/stores' myStore.setPath('/books/<book_id>') $effect(() => { if (myStore.value) { myStore.update() } })</script><input type="text" bind:value={myStore.value.bookName}
Two problems:
- I must access the store's props like
myStore.value.bookName
, instead of the cleanermyStore.bookName
. - The
$effect
rune must be in the page and not in the function that creates the store, because$effect
can only be called during component initialization, otherwise you get an error.
Overall the Svelte 4 way of doing it was much cleaner and nice to work with and I refuse to believe that you can't do the same thing with the new and supposedly improved store system.