Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/rich-turtles-learn.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'svelte': patch
---

fix: ensure derived reactions have their version kept in sync with the reactive graph
23 changes: 22 additions & 1 deletion packages/svelte/src/internal/client/reactivity/deriveds.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
/** @import { Derived, Effect } from '#client' */
/** @import { Derived, Effect, Value } from '#client' */
import { DEV } from 'esm-env';
import { CLEAN, DERIVED, DIRTY, EFFECT_HAS_DERIVED, MAYBE_DIRTY, UNOWNED } from '../constants.js';
import {
Expand Down Expand Up @@ -151,6 +151,25 @@ function execute_derived(derived) {
return value;
}

/**
* @param {Value} value
*/
function sync_reaction_versions(value) {
var reactions = value.reactions;

if (reactions !== null) {
for (var i = 0; i < reactions.length; i++) {
var reaction = reactions[i];
if ((value.f & UNOWNED) === 0 && value.wv > reaction.wv) {
reaction.wv = value.wv;
}
if ((reaction.f & DERIVED) !== 0) {
sync_reaction_versions(/** @type {Derived} */ (reaction));
}
}
}
}

/**
* @param {Derived} derived
* @returns {void}
Expand All @@ -165,5 +184,7 @@ export function update_derived(derived) {
if (!derived.equals(value)) {
derived.v = value;
derived.wv = increment_write_version();
} else {
sync_reaction_versions(derived);
}
}
2 changes: 1 addition & 1 deletion packages/svelte/src/internal/client/runtime.js
Original file line number Diff line number Diff line change
Expand Up @@ -206,7 +206,7 @@ export function check_dirtiness(reaction) {
update_derived(/** @type {Derived} */ (dependency));
}

if (dependency.wv > reaction.wv) {
if (dependency.wv > reaction.wv || (reaction.f & DIRTY) !== 0) {
return true;
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { flushSync } from 'svelte';
import { test } from '../../test';

export default test({
async test({ assert, target, logs }) {
let [btn1] = target.querySelectorAll('button');

btn1?.click();
flushSync();

btn1?.click();
flushSync();

logs.length = 0;

btn1?.click();
flushSync();

assert.deepEqual(logs, ['a', { value: 1 }]);

logs.length = 0;

btn1?.click();
flushSync();

assert.deepEqual(logs, ['a', { value: 2 }, 'b', { a: 2 }, 'c', { b: 2 }]);
}
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<script>
let trigger = $state(false)
let index = 0;

let a = $derived.by(() => {
trigger;
const value = [1,2,1][index++%3];
console.log("a", { value });
return value
})

let b = $derived.by(() => {
console.log("b", { a });
return a;
});

let c = $derived.by(() => {
console.log("c", { b });
return undefined;
});

$effect(() => {
c;
console.log('effect')
});
</script>

<button onclick={() => trigger = !trigger}>invalidate</button>
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,4 @@
// resulting in the same value should not prevent pending render effects from updating
z;
y = 0;
}}>{z}</button>
}}>{z}</button>
Loading