diff --git a/.changeset/wild-parents-begin.md b/.changeset/wild-parents-begin.md new file mode 100644 index 0000000000..ac98fa0e3e --- /dev/null +++ b/.changeset/wild-parents-begin.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: ensure custom element attribute/prop changes are in their own context diff --git a/packages/svelte/src/internal/client/dom/elements/attributes.js b/packages/svelte/src/internal/client/dom/elements/attributes.js index 30c7712f1e..545cab20f8 100644 --- a/packages/svelte/src/internal/client/dom/elements/attributes.js +++ b/packages/svelte/src/internal/client/dom/elements/attributes.js @@ -7,6 +7,12 @@ import * as w from '../../warnings.js'; import { LOADING_ATTR_SYMBOL } from '../../constants.js'; import { queue_idle_task, queue_micro_task } from '../task.js'; import { is_capture_event, is_delegated, normalize_attribute } from '../../../../utils.js'; +import { + active_effect, + active_reaction, + set_active_effect, + set_active_reaction +} from '../../runtime.js'; /** * The value/checked attribute in the template actually corresponds to the defaultValue property, so we need @@ -145,10 +151,24 @@ export function set_xlink_attribute(dom, attribute, value) { * @param {any} value */ export function set_custom_element_data(node, prop, value) { - if (get_setters(node).includes(prop)) { - node[prop] = value; - } else { - set_attribute(node, prop, value); + // We need to ensure that setting custom element props, which can + // invoke lifecycle methods on other custom elements, does not also + // associate those lifecycle methods with the current active reaction + // or effect + var previous_reaction = active_reaction; + var previous_effect = active_effect; + + set_active_reaction(null); + set_active_effect(null); + try { + if (get_setters(node).includes(prop)) { + node[prop] = value; + } else { + set_attribute(node, prop, value); + } + } finally { + set_active_reaction(previous_reaction); + set_active_effect(previous_effect); } } diff --git a/packages/svelte/tests/runtime-browser/custom-elements-samples/element-effect-context/_config.js b/packages/svelte/tests/runtime-browser/custom-elements-samples/element-effect-context/_config.js new file mode 100644 index 0000000000..3cf7a66df1 --- /dev/null +++ b/packages/svelte/tests/runtime-browser/custom-elements-samples/element-effect-context/_config.js @@ -0,0 +1,24 @@ +import { test } from '../../assert'; +const tick = () => Promise.resolve(); + +export default test({ + async test({ assert, target }) { + target.innerHTML = ''; + await tick(); + await tick(); + + /** @type {any} */ + const el = target.querySelector('my-app'); + const button = el.shadowRoot.querySelector('button'); + const p = el.shadowRoot.querySelector('my-tracking').shadowRoot.querySelector('p'); + + assert.equal(button.innerHTML, '0'); + assert.equal(p.innerHTML, 'false'); + + button.click(); + await tick(); + + assert.equal(button.innerHTML, '1'); + assert.equal(p.innerHTML, 'false'); + } +}); diff --git a/packages/svelte/tests/runtime-browser/custom-elements-samples/element-effect-context/main.svelte b/packages/svelte/tests/runtime-browser/custom-elements-samples/element-effect-context/main.svelte new file mode 100644 index 0000000000..fa35c8a086 --- /dev/null +++ b/packages/svelte/tests/runtime-browser/custom-elements-samples/element-effect-context/main.svelte @@ -0,0 +1,31 @@ + + + + + + + +