Description
Describe the problem
I'm digging the new reactivity system of runes, but there's a sticking point to me, and others I've come across. There's a clunky awkwardness when passing formulas meant for $derived
into objects. I used a class as an example here, but I reckon this will be encountered in a lot more contexts like passing props down to components, use directives, functions that return reactive objects or values, etc.
This Calculation class is meant to be told what kind of calculation to do in its construction. See the awkwardness here:
let a = $state(1)
let b = $state(2)
class Calculation {
#formula = $state()
output = $derived(this.#formula())
constructor(formula) {
this.#formula = formula
}
}
const adder = new Calculation(() => a + b)
// adder.output would be 3 to start
const multiplier = new Calculation(() => a * b)
// multiplier.output would be 2 to start
There is another way to accomplish this without using $derived
, by using the $state
able formula function directly. Even though it accomplishes the same thing, and is slightly cleaner, it's still kind of clunky because it looks like you are calling a function, when really you want to be thinking of it in terms of a derived value. This makes me think about the original reason for sprinkling Svelte magic on top of signals, to get away from "calling" signals in order to get their values.
class Calculation {
formula = $state()
constructor(formula) {
this.formula = formula
}
}
const adder = new Calculation(() => a + b)
// adder.formula() is 3
Describe the proposed solution
A reactive $derived
can be implemented simply by leveraging the fact that $derived
is not assignable, and making it assignable in such a way that it swaps its reactive function upon a new assignment. But since we may not want to break intuitions and the importance of immutability, we can keep the fundamental immutability preserved in the base$derived
and introduce an alternative kind of $derived
, like a $derived.mutable(...)
.
Here is an example of a mutable $derived:
class Calculation {
output = $derived.mutable(/* some default reactive function */)
constructor(formula) {
this.output = formula
}
}
const adder = new Calculation(() => a + b)
This would make life much easier, cleaner, and I think lines up nicely with the Svelte ethos of "Why not simplify this?" while not taking the power and flexibility away.
In short,
- let stateFn = $state(someFn)
- const value = $derived(stateFn())
+ let value = $derived.mutable(someFn)
I think this would be really handy, particularly for classes. What do you all think?
Importance
would make my life easier