Quantcast
Channel: Active questions tagged svelte - Stack Overflow
Viewing all articles
Browse latest Browse all 1546

Why do I have to use $derived when destructuring a class with states?

$
0
0

I am writing a Sveltekit app with Svelte 5 after taking a course from Niklas Fischer. The pattern is as follows: Mostly all state is contained in a global State class. In routes/+svelte.layout.ts the State class is instantiated and then put in a context with setContext. In various +page.svelte files the State is conveniently obtained with state = getContext(key). Quite often the State is destructured, e.g. let { statevar1, statevar2 } = $derived(state). It must be $derived to work, just destructuring the state does not work, nor destructuring $state(state). I can understand whatlet doubled = $derived(count*2)means, but here we are not changing the state variables, and worse, we may not change them later in the code, as you may not assign to derived values.

I wrote a small test to find out what works and what not. In test.svelte.ts

class Counter {    cnt = $state(1);    constructor(start: number) {        this.cnt = start;    }    inc() {        console.log('inc before', this.cnt);        this.cnt += 1;        console.log('inc after', this.cnt);    }    init() {        console.log('init before', this.cnt);        this.cnt = 100;        console.log('init after', this.cnt);    }}export const counter1 = new Counter(10);export const counter2 = $state({    cnt: 50});

In +page.svelte

<script lang="ts">    import { counter1, counter2 } from './test.svelte';    let { cnt: cnt1a } = counter1;    let { cnt: cnt1b } = $state(counter1);    let { cnt: cnt1c } = $derived(counter1);    let { cnt: cnt2a } = counter2;    let { cnt: cnt2b } = $state(counter2);    let { cnt: cnt2c } = $derived(counter2);</script><h1>Counter1</h1><h1>{counter1.cnt} works</h1><h1>{cnt1a} fails</h1><h1>{cnt1b} fails</h1><h1>{cnt1c} works</h1><button onclick={() => counter1.init()}>init1 works</button><button onclick={counter1.init}>init2 fails</button><button onclick={() => counter1.inc()}>inc1 works</button><button onclick={counter1.inc}>inc2 fails</button><h1>Counter2</h1><h1>{counter2.cnt} works</h1><h1>{cnt2a} fails</h1><h1>{cnt2b} fails</h1><h1>{cnt2c} works</h1><button onclick={() => counter2.cnt++}>inc2</button>

When this page runs, the buttons init2 and inc2 do not work, because class methods can not be passed as function arguments (what a pity, I assume this is due to JS, not Svelte). With init1 and inc1 only the counter counter1.cnt is reactive, and the derived counter cnt1c. But cnt2b is not, as I had assumed. And cnt1a is also not reactive, which indicates that destructuring strips off the reactivity. Why is all that so?

In additon: In the class I must initialize with some value, which is immediately overwritten by the constructor. And I must initialize with $state(), but I must not use $state in the constructor, i.e. I may not write this.cnt=$state(start). I have not even tried to call new Counter() with a $stated variable...


Viewing all articles
Browse latest Browse all 1546

Trending Articles



<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>