fromStore not reactive in a class? #13998
-
I'm trying to migrate a dexie-based app to svelte 5, and I'm experiencing a lot more headache than happiness. For context, Dexie's reactive Here's a not-too-trivial example, which is currently in a import { derived, fromStore, writable, type Readable } from 'svelte/store'
import { loadSettings } from '../queries/settings'
import { dexieStore } from './dexieStore'
const darkQuery = window.matchMedia('(prefers-color-scheme: dark)')
const osMediaStore = writable<'dark' | 'light'>(darkQuery.matches ? 'dark' : 'light')
darkQuery.addEventListener('change', (event) => {
osMediaStore.set(event.matches ? 'dark' : 'light')
})
export function deriveTheme(): Readable<'dark' | 'light'> {
const settings = dexieStore(loadSettings)
return derived([settings, osMediaStore], ([settings, osMedia]) => {
if (settings?.theme === 'system') {
return osMedia
} else {
return settings?.theme ?? 'light'
}
})
} This code listens to changes on a The easiest way to migrate this code to runes mode is to add a single global at the end that wraps export const themeState = fromStore(deriveTheme()) Inside my components, I can import themeState and use a simple $derived rune to get my expression. const theme = $derived(themeState.current) This works fine, so yay! But I hear Svelte 5 is amazing because we can use runes in global .svelte.ts files. I know my dexie store isn't going away, since rxjs is the reactive protocol Dexie conforms to, but surely I can replace the osMediaStore with a $state rune, right? In fact, I haven't found any way to create derived state that depends on a osMediaState $state rune and a store inside a global file. After getting extremely confused by the fact that neither $derived nor $effect work in global files (something that needs to be better documented), I realized that I probably need to use reactive classes, which allow $derived runes on fields. This is the closest I've gotten: import { fromStore } from 'svelte/store'
import { loadSettings } from '../queries/settings'
import { dexieStore } from '../stores/dexieStore'
class ThemeState {
#darkQuery = window.matchMedia('(prefers-color-scheme: dark)')
#osTheme: 'light' | 'dark' = $state(this.#darkQuery.matches ? 'dark' : 'light')
#settingsQuery = fromStore(dexieStore(loadSettings))
#settings = $derived(this.#settingsQuery.current)
current = $derived(
this.#settings?.theme === 'system' ? this.#osTheme : (this.#settings?.theme ?? this.#osTheme),
)
constructor() {
this.#darkQuery.addEventListener('change', (event) => {
this.#osTheme = event.matches ? 'dark' : 'light'
})
}
}
export const themeState = new ThemeState() (The My testing indicates that the I think either:
The only way I've been able to mix the import { loadSettings } from '../queries/settings'
import { dexieStore } from '../stores/dexieStore'
class ThemeState {
#darkQuery = window.matchMedia('(prefers-color-scheme: dark)')
#osTheme: 'light' | 'dark' = $state(this.#darkQuery.matches ? 'dark' : 'light')
#settingsQuery = dexieStore(loadSettings)
#userTheme: 'light' | 'dark' | 'system' = $state('system')
current = $derived(this.#userTheme === 'system' ? this.#osTheme : this.#userTheme)
constructor() {
this.#darkQuery.addEventListener('change', (event) => {
this.#osTheme = event.matches ? 'dark' : 'light'
})
this.#settingsQuery.subscribe((value) => {
console.log(value)
this.#userTheme = value.theme
})
}
}
export const themeState = new ThemeState() While this is work, it smells. The original pure-store version is actually easier to read, and that |
Beta Was this translation helpful? Give feedback.
Replies: 1 comment 2 replies
-
Looks like a bug, opened an issue: |
Beta Was this translation helpful? Give feedback.
Fix was released in v5.1.4.