fix: flush eager effects in production (#18107)

While working on #18106 I noticed that we're not adding eager effects
inside `mark_reactions` when `DEV` is `false`. As a result production
build could have `$state.eager` or `$state.pending` not working
correctly.

~No test because I can't get Vitest to not run with `DEV` being `true`.~
added a test
pull/18163/head
Simon H 2 months ago committed by GitHub
parent 9521b9f3dc
commit 90a70cb012
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -0,0 +1,5 @@
---
'svelte': patch
---
fix: flush eager effects in production

@ -47,7 +47,7 @@ import { proxy } from '../proxy.js';
import { execute_derived } from './deriveds.js';
import { set_signal_status, update_derived_status } from './status.js';
/** @type {Set<any>} */
/** @type {Set<Effect>} */
export let eager_effects = new Set();
/** @type {Map<Source, any>} */
@ -266,12 +266,6 @@ export function flush_eager_effects() {
eager_effects_deferred = false;
for (const effect of eager_effects) {
// Mark clean inspect-effects as maybe dirty and then check their dirtiness
// instead of just updating the effects - this way we avoid overfiring.
if ((effect.f & CLEAN) !== 0) {
set_signal_status(effect, MAYBE_DIRTY);
}
if (is_dirty(effect)) {
update_effect(effect);
}
@ -338,12 +332,6 @@ function mark_reactions(signal, status, updated_during_traversal) {
// In legacy mode, skip the current effect to prevent infinite loops
if (!runes && reaction === active_effect) continue;
// Inspect effects need to run immediately, so that the stack trace makes sense
if (DEV && (flags & EAGER_EFFECT) !== 0) {
eager_effects.add(reaction);
continue;
}
var not_dirty = (flags & DIRTY) === 0;
// don't set a DIRTY reaction to MAYBE_DIRTY
@ -351,7 +339,12 @@ function mark_reactions(signal, status, updated_during_traversal) {
set_signal_status(reaction, status);
}
if ((flags & DERIVED) !== 0) {
if ((flags & EAGER_EFFECT) !== 0) {
// Eager effects need to run immediately:
// - for $inspect so that the stack trace makes sense
// - for $state.eager because they might be without an effect parent
eager_effects.add(/** @type {Effect} */ (reaction));
} else if ((flags & DERIVED) !== 0) {
var derived = /** @type {Derived} */ (reaction);
batch_values?.delete(derived);

@ -0,0 +1,23 @@
import { tick } from 'svelte';
import { test } from '../../test';
export default test({
async test({ assert, target }) {
await tick();
const [increment, shift] = target.querySelectorAll('button');
increment.click();
await tick();
assert.htmlEqual(
target.innerHTML,
`<button>clicks: 0 - 0 - 0</button> <button>shift</button> <p>true - true</p>`
);
shift.click();
await tick();
assert.htmlEqual(
target.innerHTML,
`<button>clicks: 1 - 1 - 1</button> <button>shift</button> <p>false - false</p>`
);
}
});

@ -0,0 +1,22 @@
<script>
let count = $state(0);
const delayedCount = $derived(await push(count));
const derivedCount = $derived(count);
let resolvers = [];
function push(value) {
if (!value) return value;
const { promise, resolve } = Promise.withResolvers();
resolvers.push(() => resolve(value));
return promise;
}
</script>
<button onclick={() => count += 1}>
clicks: {count} - {delayedCount} - {derivedCount}
</button>
<button onclick={() => resolvers.shift()?.()}>shift</button>
<p>{$state.eager(count) !== count} - {$state.eager(derivedCount) !== derivedCount}</p>
Loading…
Cancel
Save