Skip to content
Merged
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/light-pens-watch.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'svelte': patch
---

fix: prevent reactive snippet from reinitializing unnecessarily
Original file line number Diff line number Diff line change
Expand Up @@ -1734,18 +1734,20 @@ export const template_visitors = {
if (node.argument) {
args.push(b.thunk(/** @type {import('estree').Expression} */ (context.visit(node.argument))));
}
const snippet_function = /** @type {import('estree').Expression} */ (

let snippet_function = /** @type {import('estree').Expression} */ (
context.visit(node.expression)
);
const init = b.call(
context.state.options.dev ? b.call('$.validate_snippet', snippet_function) : snippet_function,
...args
);
if (context.state.options.dev) {
snippet_function = b.call('$.validate_snippet', snippet_function);
}

if (is_reactive) {
context.state.init.push(b.stmt(b.call('$.snippet_effect', b.thunk(init))));
context.state.init.push(
b.stmt(b.call('$.snippet_effect', b.thunk(snippet_function), ...args))
);
} else {
context.state.init.push(b.stmt(init));
context.state.init.push(b.stmt(b.call(snippet_function, ...args)));
}
},
AnimateDirective(node, { state, visit }) {
Expand Down
11 changes: 8 additions & 3 deletions packages/svelte/src/internal/client/render.js
Original file line number Diff line number Diff line change
Expand Up @@ -3170,13 +3170,18 @@ export function sanitize_slots(props) {
}

/**
* @param {() => void} create_snippet
* @param {() => Function} get_snippet
* @param {Node} node
* @param {() => any} args
* @returns {void}
*/
export function snippet_effect(create_snippet) {
export function snippet_effect(get_snippet, node, args) {
const block = create_snippet_block();
render_effect(() => {
create_snippet();
// Only rerender when the snippet function itself changes,
// not when an eagerly-read prop inside the snippet function changes
const snippet = get_snippet();
untrack(() => snippet(node, args));
return () => {
if (block.d !== null) {
remove(block.d);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import { test } from '../../test';

export default test({
html: `
<p>snippet: 0</p>
<button>toggle</button>
<button>increase count</button>
`,
props: {
get log() {
return [];
}
},

async test({ assert, target, component }) {
const [toggle, increment] = target.querySelectorAll('button');

await increment?.click();
assert.htmlEqual(
target.innerHTML,
`
<p>snippet: 1</p>
<button>toggle</button>
<button>increase count</button>
`
);
assert.deepEqual(component.log, []);

await toggle?.click();
assert.htmlEqual(
target.innerHTML,
`
<p>component: 1</p>
<button>toggle</button>
<button>increase count</button>
`
);
assert.deepEqual(component.log, [1]);

await increment?.click();
assert.htmlEqual(
target.innerHTML,
`
<p>component: 2</p>
<button>toggle</button>
<button>increase count</button>
`
);
assert.deepEqual(component.log, [1]);

await toggle?.click();
assert.htmlEqual(
target.innerHTML,
`
<p>snippet: 2</p>
<button>toggle</button>
<button>increase count</button>
`
);
assert.deepEqual(component.log, [1]);
}
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<script>
let { count, log } = $props();
log.push(count);
</script>

<p>component: {count}</p>
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<script>
import Inner from "./inner.svelte";

let { log } = $props();

let count = $state(0);
let show_foo = $state(true);
let snippet = $derived(show_foo ? foo : bar);
</script>

{#snippet foo({count})}
<p>snippet: {count}</p>
{/snippet}

{#snippet bar(props)}
<Inner {...props}></Inner>
{/snippet}

{@render snippet({ count, log })}

<button onclick={() => show_foo = !show_foo}>toggle</button>
<button onclick={() => count++}>increase count</button>