mirror of https://github.com/sveltejs/svelte
fix: unskip branches of earlier batches after commit (#18048)
Fixes #17571 where the situation is the following: A derived creates a new query. That query initializes loading with true. This means the if block is marked for destruction (therefore effects inside branch are skipped), but it's not doing that yet because the query promise is pending. Then query resolves and loading is set back to false right before resolving, but it's not the same tick so `loading=false` is a separate thing. Because that later batch doesn't see any overlap with an earlier batch (the earlier batch did set loading to true but not via set but indirectly via recreating the query) it doesn't wait on it and flushes right away. Now the if block is marked as visible again but the earlier batch doesn't know that if noone unskips its branch. If we don't do that the render effect that is now dirty as part of that batch will not run.pull/18074/head
parent
cd5bda00a8
commit
0395ef0df7
@ -0,0 +1,5 @@
|
||||
---
|
||||
'svelte': patch
|
||||
---
|
||||
|
||||
fix: unskip branches of earlier batches after commit
|
||||
@ -0,0 +1,30 @@
|
||||
import { tick } from 'svelte';
|
||||
import { test } from '../../test';
|
||||
|
||||
export default test({
|
||||
async test({ assert, target }) {
|
||||
await tick();
|
||||
const [load, resolve] = target.querySelectorAll('button');
|
||||
|
||||
load.click();
|
||||
await tick();
|
||||
assert.htmlEqual(
|
||||
target.innerHTML,
|
||||
`
|
||||
<button>load</button>
|
||||
<button>resolve</button>
|
||||
`
|
||||
);
|
||||
|
||||
resolve.click();
|
||||
await tick();
|
||||
assert.htmlEqual(
|
||||
target.innerHTML,
|
||||
`
|
||||
search search search search
|
||||
<button>load</button>
|
||||
<button>resolve</button>
|
||||
`
|
||||
);
|
||||
}
|
||||
});
|
||||
@ -0,0 +1,40 @@
|
||||
<script>
|
||||
let query = $state('');
|
||||
// changing the query results in a new promise with loading initialized to true
|
||||
const promise = $derived(push(query));
|
||||
|
||||
const resolvers = [];
|
||||
function push(value) {
|
||||
if (!value) return Promise.resolve(value);
|
||||
|
||||
const { promise, resolve } = Promise.withResolvers();
|
||||
|
||||
resolvers.push(() => {
|
||||
// before resolving, set loading to false - this makes it run in a different batch
|
||||
loading = false;
|
||||
resolve(value);
|
||||
});
|
||||
|
||||
let loading = $state(true);
|
||||
Object.defineProperty(promise, 'loading', {
|
||||
get() {
|
||||
return loading;
|
||||
}
|
||||
});
|
||||
|
||||
return promise
|
||||
}
|
||||
</script>
|
||||
|
||||
{query} {await promise}
|
||||
|
||||
{#if !promise.loading}
|
||||
{query}
|
||||
{/if}
|
||||
|
||||
{#if !promise.loading}
|
||||
{await query}
|
||||
{/if}
|
||||
|
||||
<button onclick={() => query = 'search'}>load</button>
|
||||
<button onclick={() => resolvers.shift()?.()}>resolve</button>
|
||||
Loading…
Reference in new issue