feat: allow deriveds to reference their own values#17666
feat: allow deriveds to reference their own values#17666Rich-Harris wants to merge 4 commits intomainfrom
Conversation
🦋 Changeset detectedLatest commit: c32b293 The changes in this PR will be included in the next version bump. This PR includes changesets to release 1 package
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
|
|
How different is this from using an effect? var score = $state(0);
var highscore = $derived(Math.max(score, highscore || 0));var score = $state(0);
var highscore = $state(0);
$effect(()=>{
if(score>highscore) {
highscore=score;
}
})This feels like a small optimization for a rare case and i wouldn't want to give up any safety for the general case just for this. |
|
The effect variant updates only in the next tick, while derived can update right away if there is a demand. I more like #13250 design - it feels more natural var score = $state(0);
var highscore = $derived.by((highscore = 0) => Math.max(score, highscore));Though I guess both will confuse TS. |
|
TS confusion is a good point - with |
|
What I like about this approach over #13250 is that there's no new surface area — nothing to learn, it Just Works. TypeScript will complain about use before assignment but as you say there are two options to solve it ( There may be other arguments in favour of the explicit previous value that I'm not considering |
That is only partially true because realistically you're hit with the TS error right away, wondering "am I doing this right even?" and then you gotta learn about |
|
Here's something you can do with this approach that I'm fairly sure you can't do with an explicit previous value: <script>
var c = $derived(Math.round(((f || 32) - 32) * 5 / 9));
var f = $derived(Math.round(((c || 0) * 9 / 5) + 32));
</script>
<label>
<input type="number" bind:value={c}>
temperature in celsius
</label>
<label>
<input type="number" bind:value={f}>
temperature in fahrenheit
</label> |
|
Just my 2 cents: angular has an explicit split between the source ('score' in the initial example) and the executed func. And provides both previous values: source and derivation. It's more verbose, but it's also less frequent to use and pretty powerful. Overall, IMHO it works really well. https://angular.dev/guide/signals/linked-signal#accounting-for-previous-state PS: relevant discussion as well 'angular/angular#59580' |
#17646 got me thinking: why don't we allow deriveds to reference their own values? It's unusual for it to be useful, but there are arguably valid use cases, at least now that deriveds are writable — things like this:
That works on this branch, though one test is failing for reasons I don't really understand. Won't invest any time into figuring it out unless we decide that this does in fact make sense.
Before submitting the PR, please make sure you do the following
feat:,fix:,chore:, ordocs:.packages/svelte/src, add a changeset (npx changeset).Tests and linting
pnpm testand lint the project withpnpm lint