fix: depend on reads of deriveds created within reaction (async mode) (#16823)

* fix: depend on reads of deriveds created within reaction (async mode)

As part of https://github.com/sveltejs/kit/pull/14481 we discovered that deriveds created within reactions and reading from them in that same reaction is actually useful in some cases, as such a use case we couldn't imagine yet in #15564 has appeared.

We think it's ultimately better to rerun on those cases, so we're going to make this change in async mode (that way the behavior doesn't change unless you have enabled the experimental flag)

* fix tests
pull/16825/head
Simon H 1 day ago committed by GitHub
parent 7d9962a572
commit 5e6fed6bab
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -0,0 +1,5 @@
---
'svelte': patch
---
fix: depend on reads of deriveds created within reaction (async mode)

@ -29,7 +29,7 @@ import * as w from '../warnings.js';
import { async_effect, destroy_effect } from './effects.js'; import { async_effect, destroy_effect } from './effects.js';
import { inspect_effects, internal_set, set_inspect_effects, source } from './sources.js'; import { inspect_effects, internal_set, set_inspect_effects, source } from './sources.js';
import { get_stack } from '../dev/tracing.js'; import { get_stack } from '../dev/tracing.js';
import { tracing_mode_flag } from '../../flags/index.js'; import { async_mode_flag, tracing_mode_flag } from '../../flags/index.js';
import { Boundary } from '../dom/blocks/boundary.js'; import { Boundary } from '../dom/blocks/boundary.js';
import { component_context } from '../context.js'; import { component_context } from '../context.js';
import { UNINITIALIZED } from '../../../constants.js'; import { UNINITIALIZED } from '../../../constants.js';
@ -231,7 +231,7 @@ export function async_derived(fn, location) {
export function user_derived(fn) { export function user_derived(fn) {
const d = derived(fn); const d = derived(fn);
push_reaction_value(d); if (!async_mode_flag) push_reaction_value(d);
return d; return d;
} }

@ -0,0 +1,74 @@
import { flushSync } from 'svelte';
import { test } from '../../test';
export default test({
// In non-async mode we're not reacting to deriveds read in the same context they're defined in
skip_no_async: true,
test({ assert, target, logs }) {
const [a, b] = target.querySelectorAll('button');
flushSync(() => a?.click());
assert.htmlEqual(
target.innerHTML,
`
<button>a</button>
<button>b</button>
<p>1/0</p
`
);
flushSync(() => a?.click());
assert.htmlEqual(
target.innerHTML,
`
<button>a</button>
<button>b</button>
<p>2/0</p
`
);
flushSync(() => b?.click());
assert.htmlEqual(
target.innerHTML,
`
<button>a</button>
<button>b</button>
<p>2/1</p
`
);
flushSync(() => b?.click());
assert.htmlEqual(
target.innerHTML,
`
<button>a</button>
<button>b</button>
<p>2/2</p
`
);
assert.deepEqual(logs, [
// init
'a',
'b',
'effect a',
'effect b',
// click a
'a',
'effect a',
// click a
'a',
'effect a',
// click b
'a',
'b',
'effect a',
'effect b',
// click b
'a',
'b',
'effect a',
'effect b'
]);
}
});

@ -0,0 +1,30 @@
<script>
let object = $state.raw({ a: 0, b: 0 });
function a() {
console.log('a');
return object.a;
}
function b() {
console.log('b');
let double = $derived(object.b)
return double;
}
$effect(() => {
object.a;
console.log('effect a');
})
$effect(() => {
const b = $derived(object.b);
b;
console.log('effect b');
})
</script>
<button onclick={() => object = { ...object, a: object.a + 1 }}>a</button>
<button onclick={() => object = { ...object, b: object.b + 1 }}>b</button>
<p>{a()}/{b()}</p>

@ -13,10 +13,11 @@ export default test({
target.innerHTML, target.innerHTML,
` `
<button>increment</button> <button>increment</button>
<p>1/2</p <p>1/2</p>
<p>1/2</p>
` `
); );
assert.deepEqual(logs, [0, 0]); assert.deepEqual(logs, [0, 0, 0, 0]);
} }
}); });

@ -17,10 +17,14 @@
$effect(() => { $effect(() => {
foo = new Foo(); foo = new Foo();
}); });
let bar = $derived(new Foo());
</script> </script>
<button onclick={() => foo.increment()}>increment</button> <button onclick={() => {foo.increment(); bar.increment()}}>increment</button>
{#if foo} {#if foo}
<p>{foo.value}/{foo.double}</p> <p>{foo.value}/{foo.double}</p>
{/if} {/if}
<p>{bar.value}/{bar.double}</p>

Loading…
Cancel
Save