svelte: A .svelte.ts file does not create reactivity across numerous $derived updates
Describe the bug
I could not get chained reactivity to work in a .svelte.ts class. The goal is to have all state stored and modified in a single place, with varying views reacting to the updates. Use cases include Svelte components with an AppData instance in their props that rerender when $state and $derived data in AppData are updated, while others only receive updated props from a parent component that holds an AppData.
I tried using a class and a HOF and neither approach was successful. A single $state field can be updated in AppData and reactively rerenders views, but anything more complex than that breaks.
AppData.svelte.ts using a class AppData
export class AppData {
#currentIndex: number = $state(0)
#inspections: Array<InspectionState> = $state([])
#url: string | null = $state(null)
#currentInspection: InspectionState | null = $derived.by(() => {
if (this.#inspections[this.#currentIndex]) {
return this.#inspections[this.#currentIndex]
} else {
return null
}
})
get focusedInspection(): InspectionState | null {
return this.#currentInspection
}
get inspections(): Array<InspectionState> {
return this.#inspections
}
set url(url: string) {
this.#url = url
}
get url(): string | null {
return this.#url
}
addInspectResult(result: InspectResult): void {
this.#inspections.push({
boundingBoxes: buildBoundingBoxes(result.elements),
result,
status: InspectionStatus.retrieved,
type: result.selector ? InspectionType.selector : InspectionType.point,
})
this.#currentIndex = this.#inspections.length - 1
}
}
export function createAppData(): AppData {
let _currentIndex: number = $state(-1)
let _inspections: Array<InspectionState> = $state([])
let _url: string | null = $state(null)
let _currentInspection: InspectionState | null = $derived.by(() => {
if (_inspections[_currentIndex]) {
return _inspections[_currentIndex]
} else {
return null
}
})
$inspect(_currentIndex, _currentInspection)
return {
get focusedInspection(): InspectionState | null {
return _currentInspection
},
get url(): string | null {
return _url
},
set url(url: string | null) {
_url = url
},
addInspectResult(result: InspectResult) {
_inspections.push({
boundingBoxes: buildBoundingBoxes(result.elements),
type: result.selector ? InspectionType.selector : InspectionType.point,
status: InspectionStatus.retrieved,
result,
})
_currentIndex++
},
}
}
There’s also some weird typey things that don’t seem like a TypeScript issue. It seems like a fn like a foo(): SomeType | null
always gets type erasured to SomeType, or if there’s a return type with an optional field like type Foo = {someOptional?: {fieldsNested: boolean}}
that comes from a function return type in a .svelte.ts, TypeScript will resolve result.someOptional.fieldsNested
but understanding the result.someOptional?.fieldsNested
breaks resolution.
Are these use cases unreasonable expectations or am I going about it wrong and have some unidentified logic mishap?
Reproduction
AppData.svelte.ts using a class AppData
Logs
No response
System Info
System:
OS: macOS 14.4.1
CPU: (16) x64 Intel(R) Core(TM) i9-9880H CPU @ 2.30GHz
Memory: 1.29 GB / 16.00 GB
Shell: 5.9 - /bin/zsh
Binaries:
Node: 20.9.0 - /usr/local/bin/node
npm: 10.1.0 - /usr/local/bin/npm
pnpm: 8.6.3 - /usr/local/bin/pnpm
Browsers:
Chrome: 124.0.6367.61
Safari: 17.4.1
Severity
blocking an upgrade
About this issue
- Original URL
- State: closed
- Created 3 months ago
- Reactions: 1
- Comments: 28 (14 by maintainers)
Wdym by this? That’s not something svelte can warn you against. It’s a prop, from what svelte is concerned you might really want to reverse that array whenever you render.
And
toReversed
works, I just tested it.toReversed would not do it in place. Dumb that there isn’t a dx catch.