fix: ensure element effects are executed in the correct order (#14038)

pull/14031/head
Dominic Gannaway 6 days ago committed by GitHub
parent 3fe278a8c3
commit 253d01ec29
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -0,0 +1,5 @@
---
'svelte': patch
---
fix: ensure element effects are executed in the correct order

@ -245,8 +245,12 @@ export function BindDirective(node, context) {
const has_action_directive =
parent.type === 'RegularElement' && parent.attributes.find((a) => a.type === 'UseDirective');
context.state.after_update.push(
b.stmt(has_action_directive ? b.call('$.effect', b.thunk(call)) : call)
);
if (has_action_directive) {
context.state.init.push(
b.stmt(has_action_directive ? b.call('$.effect', b.thunk(call)) : call)
);
} else {
context.state.after_update.push(b.stmt(call));
}
}
}

@ -158,17 +158,19 @@ export function RegularElement(node, context) {
}
/** @type {typeof state} */
const element_after_update_state = { ...context.state, after_update: [] };
const element_state = { ...context.state, init: [], after_update: [] };
for (const attribute of other_directives) {
if (attribute.type === 'OnDirective') {
const handler = /** @type {Expression} */ (context.visit(attribute));
element_after_update_state.after_update.push(
b.stmt(has_use ? b.call('$.effect', b.thunk(handler)) : handler)
);
if (has_use) {
element_state.init.push(b.stmt(b.call('$.effect', b.thunk(handler))));
} else {
element_state.after_update.push(b.stmt(handler));
}
} else {
context.visit(attribute, element_after_update_state);
context.visit(attribute, element_state);
}
}
@ -401,18 +403,16 @@ export function RegularElement(node, context) {
...child_state.init,
child_state.update.length > 0 ? build_render_statement(child_state.update) : b.empty,
...child_state.after_update,
...element_after_update_state.after_update
...element_state.after_update
])
);
} else if (node.fragment.metadata.dynamic) {
context.state.init.push(...child_state.init);
context.state.init.push(...child_state.init, ...element_state.init);
context.state.update.push(...child_state.update);
context.state.after_update.push(
...child_state.after_update,
...element_after_update_state.after_update
);
context.state.after_update.push(...child_state.after_update, ...element_state.after_update);
} else {
context.state.after_update.push(...element_after_update_state.after_update);
context.state.init.push(...element_state.init);
context.state.after_update.push(...element_state.after_update);
}
if (lookup.has('dir')) {

@ -29,6 +29,6 @@ export function UseDirective(node, context) {
}
// actions need to run after attribute updates in order with bindings/events
context.state.after_update.push(b.stmt(b.call('$.action', ...args)));
context.state.init.push(b.stmt(b.call('$.action', ...args)));
context.next();
}

@ -14,6 +14,6 @@ export default test({
};
},
test({ assert }) {
assert.deepEqual(result, ['each_action', 'import_action']); // ideally this would be reversed, but it doesn't matter a whole lot
assert.deepEqual(result, ['import_action', 'each_action']);
}
});

@ -0,0 +1,7 @@
import { test } from '../../test';
export default test({
async test({ assert, logs }) {
assert.deepEqual(logs, [0, 1, 2, 3, 4, 5, 6]);
}
});

@ -0,0 +1,22 @@
<script>
function create_action() {
let index = 0;
return () => {
console.log(index++);
};
}
const content = create_action();
</script>
{#if true}
<div use:content></div>
{/if}
<div use:content></div>
{#each { length: 5 } as _}
<div>
<div use:content></div>
</div>
{/each}
Loading…
Cancel
Save