Skip to content

Svelte 5: A mutable $derived rune that allows easy passing and changing of reactive functions and dependencies #11128

Closed
@petermakeswebsites

Description

@petermakeswebsites

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 $stateable 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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions